mirror of https://github.com/weisJ/darklaf.git
weisj
5 years ago
43 changed files with 2091 additions and 350 deletions
@ -1,100 +0,0 @@ |
|||||||
/* |
|
||||||
* MIT License |
|
||||||
* |
|
||||||
* Copyright (c) 2019 Jannis Weis |
|
||||||
* |
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy |
|
||||||
* of this software and associated documentation files (the "Software"), to deal |
|
||||||
* in the Software without restriction, including without limitation the rights |
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|
||||||
* copies of the Software, and to permit persons to whom the Software is |
|
||||||
* furnished to do so, subject to the following conditions: |
|
||||||
* |
|
||||||
* The above copyright notice and this permission notice shall be included in all |
|
||||||
* copies or substantial portions of the Software. |
|
||||||
* |
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
|
||||||
* SOFTWARE. |
|
||||||
*/ |
|
||||||
package com.weis.darklaf.components; |
|
||||||
|
|
||||||
import com.weis.darklaf.decorators.PlainAction; |
|
||||||
|
|
||||||
import javax.swing.*; |
|
||||||
import java.awt.*; |
|
||||||
import java.awt.event.ActionEvent; |
|
||||||
import java.awt.event.ActionListener; |
|
||||||
import java.util.Iterator; |
|
||||||
import java.util.LinkedHashSet; |
|
||||||
import java.util.LinkedList; |
|
||||||
|
|
||||||
/** |
|
||||||
* @author Jannis Weis |
|
||||||
*/ |
|
||||||
public class TextFieldHistory extends ScrollPopupMenu implements ActionListener { |
|
||||||
|
|
||||||
private final LinkedHashSet<String> history; |
|
||||||
private JTextField textField; |
|
||||||
|
|
||||||
/** |
|
||||||
* Create Scroll Popup Menu. |
|
||||||
* |
|
||||||
* @param textField the text field. |
|
||||||
* @param length the length of the history. |
|
||||||
* @param maxH maximum height. |
|
||||||
*/ |
|
||||||
public TextFieldHistory(final JTextField textField, final int length, final int maxH) { |
|
||||||
super(maxH); |
|
||||||
this.history = new LinkedHashSet<>(length); |
|
||||||
this.textField = textField; |
|
||||||
if (textField != null) { |
|
||||||
textField.addActionListener(this); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public void setTextField(final JTextField textField) { |
|
||||||
if (this.textField != null) { |
|
||||||
textField.removeActionListener(this); |
|
||||||
} |
|
||||||
this.textField = textField; |
|
||||||
if (this.textField != null) { |
|
||||||
textField.addActionListener(this); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void actionPerformed(final ActionEvent e) { |
|
||||||
var text = textField.getText(); |
|
||||||
if (!text.isBlank()) { |
|
||||||
history.remove(text); |
|
||||||
history.add(text); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void show(final Component invoker, final int x, final int y) { |
|
||||||
if (history.size() == 0) return; |
|
||||||
super.show(invoker, x, y); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
protected void showPopup() { |
|
||||||
if (history.size() == 0) { |
|
||||||
firePopupMenuCanceled(); |
|
||||||
return; |
|
||||||
} |
|
||||||
this.removeAll(); |
|
||||||
LinkedList<String> list = new LinkedList<>(history); |
|
||||||
Iterator<String> itr = list.descendingIterator(); |
|
||||||
while (itr.hasNext()) { |
|
||||||
String item = itr.next(); |
|
||||||
add(new JMenuItem(new PlainAction(item, () -> textField.setText(item)))); |
|
||||||
} |
|
||||||
super.showPopup(); |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,40 @@ |
|||||||
|
/* |
||||||
|
* MIT License |
||||||
|
* |
||||||
|
* Copyright (c) 2019 Jannis Weis |
||||||
|
* |
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy |
||||||
|
* of this software and associated documentation files (the "Software"), to deal |
||||||
|
* in the Software without restriction, including without limitation the rights |
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||||
|
* copies of the Software, and to permit persons to whom the Software is |
||||||
|
* furnished to do so, subject to the following conditions: |
||||||
|
* |
||||||
|
* The above copyright notice and this permission notice shall be included in all |
||||||
|
* copies or substantial portions of the Software. |
||||||
|
* |
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||||
|
* SOFTWARE. |
||||||
|
*/ |
||||||
|
package com.weis.darklaf.components.text; |
||||||
|
|
||||||
|
import java.awt.event.ActionEvent; |
||||||
|
|
||||||
|
public class SearchEvent extends ActionEvent { |
||||||
|
|
||||||
|
private final String text; |
||||||
|
|
||||||
|
public SearchEvent(final Object source, final int id, final String command, final String text) { |
||||||
|
super(source, id, command); |
||||||
|
this.text = text; |
||||||
|
} |
||||||
|
|
||||||
|
public String getText() { |
||||||
|
return text; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,31 @@ |
|||||||
|
/* |
||||||
|
* MIT License |
||||||
|
* |
||||||
|
* Copyright (c) 2019 Jannis Weis |
||||||
|
* |
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy |
||||||
|
* of this software and associated documentation files (the "Software"), to deal |
||||||
|
* in the Software without restriction, including without limitation the rights |
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||||
|
* copies of the Software, and to permit persons to whom the Software is |
||||||
|
* furnished to do so, subject to the following conditions: |
||||||
|
* |
||||||
|
* The above copyright notice and this permission notice shall be included in all |
||||||
|
* copies or substantial portions of the Software. |
||||||
|
* |
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||||
|
* SOFTWARE. |
||||||
|
*/ |
||||||
|
package com.weis.darklaf.components.text; |
||||||
|
|
||||||
|
import java.util.EventListener; |
||||||
|
|
||||||
|
public interface SearchListener extends EventListener { |
||||||
|
|
||||||
|
void searchPerformed(final SearchEvent e); |
||||||
|
} |
@ -0,0 +1,119 @@ |
|||||||
|
/* |
||||||
|
* MIT License |
||||||
|
* |
||||||
|
* Copyright (c) 2019 Jannis Weis |
||||||
|
* |
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy |
||||||
|
* of this software and associated documentation files (the "Software"), to deal |
||||||
|
* in the Software without restriction, including without limitation the rights |
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||||
|
* copies of the Software, and to permit persons to whom the Software is |
||||||
|
* furnished to do so, subject to the following conditions: |
||||||
|
* |
||||||
|
* The above copyright notice and this permission notice shall be included in all |
||||||
|
* copies or substantial portions of the Software. |
||||||
|
* |
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||||
|
* SOFTWARE. |
||||||
|
*/ |
||||||
|
package com.weis.darklaf.components.text; |
||||||
|
|
||||||
|
import javax.swing.*; |
||||||
|
import javax.swing.text.Document; |
||||||
|
|
||||||
|
public class SearchTextField extends JTextField { |
||||||
|
|
||||||
|
public static final int SEARCH = 0; |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructs a new <code>TextField</code>. A default model is created, the initial string is <code>null</code>, |
||||||
|
* and the number of columns is set to 0. |
||||||
|
*/ |
||||||
|
public SearchTextField() { |
||||||
|
this(null, null, 0); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructs a new <code>JTextField</code> that uses the given text storage model and the given number of columns. |
||||||
|
* This is the constructor through which the other constructors feed. If the document is <code>null</code>, a |
||||||
|
* default model is created. |
||||||
|
* |
||||||
|
* @param doc the text storage to use; if this is <code>null</code>, a default will be provided by calling the |
||||||
|
* <code>createDefaultModel</code> method |
||||||
|
* @param text the initial string to display, or <code>null</code> |
||||||
|
* @param columns the number of columns to use to calculate the preferred width >= 0; if <code>columns</code> is |
||||||
|
* set to zero, the preferred width will be whatever naturally results from the component |
||||||
|
* implementation |
||||||
|
* @throws IllegalArgumentException if <code>columns</code> < 0 |
||||||
|
*/ |
||||||
|
public SearchTextField(final Document doc, final String text, final int columns) { |
||||||
|
super(doc, text, columns); |
||||||
|
putClientProperty("JTextField.variant", "search"); |
||||||
|
addActionListener(e -> { |
||||||
|
var list = listenerList.getListeners(SearchListener.class); |
||||||
|
var evt = new SearchEvent(SearchTextField.this, SEARCH, "search", getText()); |
||||||
|
for (var listener : list) { |
||||||
|
if (listener != null) { |
||||||
|
listener.searchPerformed(evt); |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructs a new <code>TextField</code> initialized with the specified text. A default model is created and the |
||||||
|
* number of columns is 0. |
||||||
|
* |
||||||
|
* @param text the text to be displayed, or <code>null</code> |
||||||
|
*/ |
||||||
|
public SearchTextField(final String text) { |
||||||
|
this(null, text, 0); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructs a new empty <code>TextField</code> with the specified number of columns. A default model is created |
||||||
|
* and the initial string is set to |
||||||
|
* <code>null</code>. |
||||||
|
* |
||||||
|
* @param columns the number of columns to use to calculate the preferred width; if columns is set to zero, the |
||||||
|
* preferred width will be whatever naturally results from the component implementation |
||||||
|
*/ |
||||||
|
public SearchTextField(final int columns) { |
||||||
|
this(null, null, columns); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructs a new <code>TextField</code> initialized with the specified text and columns. A default model is |
||||||
|
* created. |
||||||
|
* |
||||||
|
* @param text the text to be displayed, or <code>null</code> |
||||||
|
* @param columns the number of columns to use to calculate the preferred width; if columns is set to zero, the |
||||||
|
* preferred width will be whatever naturally results from the component implementation |
||||||
|
*/ |
||||||
|
public SearchTextField(final String text, final int columns) { |
||||||
|
this(null, text, columns); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Add a {@link SearchListener} to this search text field. |
||||||
|
* |
||||||
|
* @param listener the listen to add. |
||||||
|
*/ |
||||||
|
public void addSearchListener(final SearchListener listener) { |
||||||
|
listenerList.add(SearchListener.class, listener); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Remove a {@link SearchListener} from this search text field. |
||||||
|
* |
||||||
|
* @param listener the listener to remove |
||||||
|
*/ |
||||||
|
public void removeSearchListener(final SearchListener listener) { |
||||||
|
listenerList.remove(SearchListener.class, listener); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,174 @@ |
|||||||
|
/* |
||||||
|
* MIT License |
||||||
|
* |
||||||
|
* Copyright (c) 2019 Jannis Weis |
||||||
|
* |
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy |
||||||
|
* of this software and associated documentation files (the "Software"), to deal |
||||||
|
* in the Software without restriction, including without limitation the rights |
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||||
|
* copies of the Software, and to permit persons to whom the Software is |
||||||
|
* furnished to do so, subject to the following conditions: |
||||||
|
* |
||||||
|
* The above copyright notice and this permission notice shall be included in all |
||||||
|
* copies or substantial portions of the Software. |
||||||
|
* |
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||||
|
* SOFTWARE. |
||||||
|
*/ |
||||||
|
package com.weis.darklaf.components.text; |
||||||
|
|
||||||
|
import javax.swing.text.Document; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
/** |
||||||
|
* {@link SearchTextField} that has a popup that displays the search history. A search entry is added |
||||||
|
* |
||||||
|
* @author Jannis Weis |
||||||
|
*/ |
||||||
|
public class SearchTextFieldWithHistory extends SearchTextField { |
||||||
|
|
||||||
|
protected TextFieldHistoryPopup history; |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructs a new <code>TextField</code>. A default model is created, the initial string is <code>null</code>, |
||||||
|
* and the number of columns is set to 0. |
||||||
|
*/ |
||||||
|
public SearchTextFieldWithHistory() { |
||||||
|
this(null, null, 0); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructs a new <code>JTextField</code> that uses the given text storage model and the given number of columns. |
||||||
|
* This is the constructor through which the other constructors feed. If the document is <code>null</code>, a |
||||||
|
* default model is created. |
||||||
|
* |
||||||
|
* @param doc the text storage to use; if this is <code>null</code>, a default will be provided by calling the |
||||||
|
* <code>createDefaultModel</code> method |
||||||
|
* @param text the initial string to display, or <code>null</code> |
||||||
|
* @param columns the number of columns to use to calculate the preferred width >= 0; if <code>columns</code> is |
||||||
|
* set to zero, the preferred width will be whatever naturally results from the component |
||||||
|
* implementation |
||||||
|
* @throws IllegalArgumentException if <code>columns</code> < 0 |
||||||
|
*/ |
||||||
|
public SearchTextFieldWithHistory(final Document doc, final String text, final int columns) { |
||||||
|
super(doc, text, columns); |
||||||
|
history = new TextFieldHistoryPopup(this, 100, 800); |
||||||
|
putClientProperty("JTextField.Search.FindPopup", history); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructs a new <code>TextField</code> initialized with the specified text. A default model is created and the |
||||||
|
* number of columns is 0. |
||||||
|
* |
||||||
|
* @param text the text to be displayed, or <code>null</code> |
||||||
|
*/ |
||||||
|
public SearchTextFieldWithHistory(final String text) { |
||||||
|
this(null, text, 0); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructs a new empty <code>TextField</code> with the specified number of columns. A default model is created |
||||||
|
* and the initial string is set to |
||||||
|
* <code>null</code>. |
||||||
|
* |
||||||
|
* @param columns the number of columns to use to calculate the preferred width; if columns is set to zero, the |
||||||
|
* preferred width will be whatever naturally results from the component implementation |
||||||
|
*/ |
||||||
|
public SearchTextFieldWithHistory(final int columns) { |
||||||
|
this(null, null, columns); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructs a new <code>TextField</code> initialized with the specified text and columns. A default model is |
||||||
|
* created. |
||||||
|
* |
||||||
|
* @param text the text to be displayed, or <code>null</code> |
||||||
|
* @param columns the number of columns to use to calculate the preferred width; if columns is set to zero, the |
||||||
|
* preferred width will be whatever naturally results from the component implementation |
||||||
|
*/ |
||||||
|
public SearchTextFieldWithHistory(final String text, final int columns) { |
||||||
|
this(null, text, columns); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set the maximum height of the popup. If the size is larger than the specified maximum height the content will be |
||||||
|
* wrapped inside a scroll pane. |
||||||
|
* <p> |
||||||
|
* Note: A value of <= 0 indicates that the height should not be limited. |
||||||
|
* |
||||||
|
* @param maximumHeight the max height to use. |
||||||
|
*/ |
||||||
|
public void setMaximumHeight(final int maximumHeight) { |
||||||
|
history.setMaxHeight(maximumHeight); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the history as a list. |
||||||
|
* |
||||||
|
* @return the history. |
||||||
|
*/ |
||||||
|
public List<String> getHistory() { |
||||||
|
return history.getHistory(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Clear all entries from the history. |
||||||
|
*/ |
||||||
|
public void clearHistory() { |
||||||
|
history.clearHistory(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Add entry to the history. If the size is greater than the capacity the oldest entry will be deleted. |
||||||
|
* |
||||||
|
* @param entry the entry to add. |
||||||
|
* @see #getLength() getLength |
||||||
|
* @see #setCapacity(int) setCapacity |
||||||
|
* @see #getCapacity() getCapacity |
||||||
|
*/ |
||||||
|
public void addEntry(final String entry) { |
||||||
|
history.addEntry(entry); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the capacity of the history. |
||||||
|
* |
||||||
|
* @return the capacity. |
||||||
|
* @see #setCapacity(int) setCapacity() |
||||||
|
*/ |
||||||
|
public int getCapacity() { |
||||||
|
return history.getCapacity(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set the capacity of the history. If the size grows larger than the capacity the oldest entry will be deleted. |
||||||
|
* |
||||||
|
* @param capacity the capacity. |
||||||
|
* @throws IllegalArgumentException if capacity < 0 |
||||||
|
*/ |
||||||
|
public void setCapacity(final int capacity) throws IllegalArgumentException { |
||||||
|
history.setCapacity(capacity); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the current length of the history. |
||||||
|
* |
||||||
|
* @return the current length of the history. |
||||||
|
*/ |
||||||
|
public int getLength() { |
||||||
|
return history.getLength(); |
||||||
|
} |
||||||
|
|
||||||
|
public void setHistoryLength(final int length) { |
||||||
|
if (length < 0) throw new IllegalArgumentException("History can't have negative size"); |
||||||
|
if (length == 0) { |
||||||
|
putClientProperty("JTextField.Search.FindPopup", null); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,157 @@ |
|||||||
|
/* |
||||||
|
* MIT License |
||||||
|
* |
||||||
|
* Copyright (c) 2019 Jannis Weis |
||||||
|
* |
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy |
||||||
|
* of this software and associated documentation files (the "Software"), to deal |
||||||
|
* in the Software without restriction, including without limitation the rights |
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||||
|
* copies of the Software, and to permit persons to whom the Software is |
||||||
|
* furnished to do so, subject to the following conditions: |
||||||
|
* |
||||||
|
* The above copyright notice and this permission notice shall be included in all |
||||||
|
* copies or substantial portions of the Software. |
||||||
|
* |
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||||
|
* SOFTWARE. |
||||||
|
*/ |
||||||
|
package com.weis.darklaf.components.text; |
||||||
|
|
||||||
|
import com.weis.darklaf.components.ScrollPopupMenu; |
||||||
|
import com.weis.darklaf.decorators.PlainAction; |
||||||
|
import org.jetbrains.annotations.NotNull; |
||||||
|
|
||||||
|
import javax.swing.*; |
||||||
|
import java.awt.*; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.Collections; |
||||||
|
import java.util.Iterator; |
||||||
|
import java.util.LinkedHashMap; |
||||||
|
import java.util.LinkedList; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Map; |
||||||
|
import java.util.Set; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author Jannis Weis |
||||||
|
*/ |
||||||
|
public class TextFieldHistoryPopup extends ScrollPopupMenu implements SearchListener { |
||||||
|
|
||||||
|
private final Set<String> history; |
||||||
|
private final JTextField textField; |
||||||
|
private int capacity; |
||||||
|
|
||||||
|
/** |
||||||
|
* Create a search popup Menu. |
||||||
|
* |
||||||
|
* @param textField the text field. |
||||||
|
* @param capacity the length of the history. |
||||||
|
* @param maxH maximum height. |
||||||
|
*/ |
||||||
|
public TextFieldHistoryPopup(@NotNull final SearchTextField textField, final int capacity, final int maxH) { |
||||||
|
super(maxH); |
||||||
|
this.textField = textField; |
||||||
|
textField.addSearchListener(this); |
||||||
|
setCapacity(capacity); |
||||||
|
this.history = Collections.newSetFromMap(new LinkedHashMap<>() { |
||||||
|
protected boolean removeEldestEntry(final Map.Entry<String, Boolean> eldest) { |
||||||
|
return size() > capacity; |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the history as a list. |
||||||
|
* |
||||||
|
* @return the history. |
||||||
|
*/ |
||||||
|
public List<String> getHistory() { |
||||||
|
return new ArrayList<>(history); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the capacity of the history. |
||||||
|
* |
||||||
|
* @return the capacity. |
||||||
|
* @see #setCapacity(int) setCapacity() |
||||||
|
*/ |
||||||
|
public int getCapacity() { |
||||||
|
return capacity; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set the capacity of the history. If the size grows larger than the capacity the oldest entry will be deleted. |
||||||
|
* |
||||||
|
* @param capacity the capacity. |
||||||
|
* @throws IllegalArgumentException if capacity < 0 |
||||||
|
*/ |
||||||
|
public void setCapacity(final int capacity) throws IllegalArgumentException { |
||||||
|
if (capacity < 0) throw new IllegalArgumentException("Negative history size is not supported"); |
||||||
|
this.capacity = capacity; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the current length of the history. |
||||||
|
* |
||||||
|
* @return the current length of the history. |
||||||
|
*/ |
||||||
|
public int getLength() { |
||||||
|
return history.size(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void searchPerformed(@NotNull final SearchEvent e) { |
||||||
|
var text = e.getText(); |
||||||
|
if (!text.isBlank()) { |
||||||
|
addEntry(text); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void show(final Component invoker, final int x, final int y) { |
||||||
|
if (history.size() == 0) return; |
||||||
|
super.show(invoker, x, y); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void showPopup() { |
||||||
|
if (history.size() == 0) { |
||||||
|
firePopupMenuCanceled(); |
||||||
|
return; |
||||||
|
} |
||||||
|
this.removeAll(); |
||||||
|
LinkedList<String> list = new LinkedList<>(history); |
||||||
|
Iterator<String> itr = list.descendingIterator(); |
||||||
|
while (itr.hasNext()) { |
||||||
|
String item = itr.next(); |
||||||
|
add(new JMenuItem(new PlainAction(item, () -> textField.setText(item)))); |
||||||
|
} |
||||||
|
super.showPopup(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Add entry to the history. If the size is greater than the capacity the oldest entry will be deleted. |
||||||
|
* |
||||||
|
* @param entry the entry to add. |
||||||
|
* @see #getLength() getLength |
||||||
|
* @see #setCapacity(int) setCapacity |
||||||
|
* @see #getCapacity() getCapacity |
||||||
|
*/ |
||||||
|
public void addEntry(final String entry) { |
||||||
|
history.remove(entry); |
||||||
|
history.add(entry); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Clear all entries from the history. |
||||||
|
*/ |
||||||
|
public void clearHistory() { |
||||||
|
history.clear(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,60 @@ |
|||||||
|
/* |
||||||
|
* MIT License |
||||||
|
* |
||||||
|
* Copyright (c) 2019 Jannis Weis |
||||||
|
* |
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy |
||||||
|
* of this software and associated documentation files (the "Software"), to deal |
||||||
|
* in the Software without restriction, including without limitation the rights |
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||||
|
* copies of the Software, and to permit persons to whom the Software is |
||||||
|
* furnished to do so, subject to the following conditions: |
||||||
|
* |
||||||
|
* The above copyright notice and this permission notice shall be included in all |
||||||
|
* copies or substantial portions of the Software. |
||||||
|
* |
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||||
|
* SOFTWARE. |
||||||
|
*/ |
||||||
|
package com.weis.darklaf.ui.tabframe; |
||||||
|
|
||||||
|
import com.weis.darklaf.components.tabframe.TabFrameTab; |
||||||
|
import com.weis.darklaf.util.DarkUIUtil; |
||||||
|
import com.weis.darklaf.util.SwingXUtilities; |
||||||
|
import org.jdesktop.jxlayer.JXLayer; |
||||||
|
import org.jetbrains.annotations.NotNull; |
||||||
|
|
||||||
|
import javax.swing.*; |
||||||
|
import java.awt.event.MouseAdapter; |
||||||
|
import java.awt.event.MouseEvent; |
||||||
|
|
||||||
|
public class TabDragListener extends MouseAdapter { |
||||||
|
|
||||||
|
private final TabFrameTab tabComponent; |
||||||
|
|
||||||
|
public TabDragListener(final TabFrameTab tabComponent) { |
||||||
|
this.tabComponent = tabComponent; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void mouseDragged(@NotNull final MouseEvent e) { |
||||||
|
var th = tabComponent.getTabFrame().getTransferHandler(); |
||||||
|
if (th != null && tabComponent.getTabFrame().isDndEnabled()) { |
||||||
|
var p = e.getPoint(); |
||||||
|
p = SwingXUtilities.convertPointToParent(tabComponent.getComponent(), p); |
||||||
|
JXLayer layer = DarkUIUtil.getParentOfType(JXLayer.class, tabComponent.getComponent()); |
||||||
|
p = SwingUtilities.convertPoint(layer != null ? layer : tabComponent.getComponent().getParent(), |
||||||
|
p, tabComponent.getTabFrame()); |
||||||
|
tabComponent.getTabFrame().initTransfer(tabComponent.getOrientation(), tabComponent.getIndex()); |
||||||
|
th.exportAsDrag(tabComponent.getTabFrame(), |
||||||
|
new MouseEvent(tabComponent.getTabFrame(), e.getID(), e.getWhen(), e.getModifiersEx(), |
||||||
|
p.x, p.y, e.getClickCount(), e.isPopupTrigger(), e.getButton()), |
||||||
|
TransferHandler.MOVE); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,508 @@ |
|||||||
|
/* |
||||||
|
* MIT License |
||||||
|
* |
||||||
|
* Copyright (c) 2019 Jannis Weis |
||||||
|
* |
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy |
||||||
|
* of this software and associated documentation files (the "Software"), to deal |
||||||
|
* in the Software without restriction, including without limitation the rights |
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||||
|
* copies of the Software, and to permit persons to whom the Software is |
||||||
|
* furnished to do so, subject to the following conditions: |
||||||
|
* |
||||||
|
* The above copyright notice and this permission notice shall be included in all |
||||||
|
* copies or substantial portions of the Software. |
||||||
|
* |
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||||
|
* SOFTWARE. |
||||||
|
*/ |
||||||
|
package com.weis.darklaf.ui.tabframe; |
||||||
|
|
||||||
|
import com.weis.darklaf.components.alignment.Alignment; |
||||||
|
import com.weis.darklaf.components.tabframe.JTabFrame; |
||||||
|
import com.weis.darklaf.components.tabframe.TabFramePopup; |
||||||
|
import com.weis.darklaf.components.tabframe.TabFrameTab; |
||||||
|
import com.weis.darklaf.components.tabframe.TabFrameUI; |
||||||
|
import com.weis.darklaf.util.ImageUtil; |
||||||
|
import org.jetbrains.annotations.Contract; |
||||||
|
import org.jetbrains.annotations.NotNull; |
||||||
|
|
||||||
|
import javax.swing.*; |
||||||
|
import java.awt.*; |
||||||
|
import java.awt.datatransfer.DataFlavor; |
||||||
|
import java.awt.datatransfer.Transferable; |
||||||
|
import java.awt.datatransfer.UnsupportedFlavorException; |
||||||
|
import java.awt.dnd.DragGestureEvent; |
||||||
|
import java.awt.dnd.DragGestureListener; |
||||||
|
import java.awt.dnd.DragGestureRecognizer; |
||||||
|
import java.awt.dnd.DragSource; |
||||||
|
import java.awt.dnd.DragSourceContext; |
||||||
|
import java.awt.dnd.DragSourceDragEvent; |
||||||
|
import java.awt.dnd.DragSourceDropEvent; |
||||||
|
import java.awt.dnd.DragSourceEvent; |
||||||
|
import java.awt.dnd.DragSourceListener; |
||||||
|
import java.awt.dnd.DropTargetDragEvent; |
||||||
|
import java.awt.dnd.DropTargetDropEvent; |
||||||
|
import java.awt.dnd.DropTargetEvent; |
||||||
|
import java.awt.dnd.DropTargetListener; |
||||||
|
import java.awt.event.InputEvent; |
||||||
|
import java.awt.event.MouseEvent; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* @author Robert Futrell |
||||||
|
* @author Jannis Weis |
||||||
|
*/ |
||||||
|
public class TabFrameTransferHandler extends TransferHandler implements DropTargetListener, SwingConstants { |
||||||
|
|
||||||
|
private static final String MIME_TYPE = DataFlavor.javaJVMLocalObjectMimeType |
||||||
|
+ ";class=com.weis.darklaf.components.tabframe.TabFrame"; |
||||||
|
private static TabbedPaneDragGestureRecognizer recognizer = null; |
||||||
|
private final Timer timer; |
||||||
|
private final Timer startTimer; |
||||||
|
/** |
||||||
|
* The location of the mouse cursor throughout the drag-and-drop. This is here because of a deficiency in |
||||||
|
* TransferHandler's design; you have no way of knowing the exact drop location in the component with a plain |
||||||
|
* TransferHandler unless you implement DropTargetListener and get it that way. |
||||||
|
*/ |
||||||
|
protected Point mouseLocation; |
||||||
|
private DataFlavor tabFlavor; |
||||||
|
private TabTransferable currentTransferable; |
||||||
|
private JTabFrame lastTabFrame; |
||||||
|
|
||||||
|
|
||||||
|
public TabFrameTransferHandler() { |
||||||
|
try { |
||||||
|
tabFlavor = new DataFlavor(MIME_TYPE); |
||||||
|
} catch (ClassNotFoundException ignored) { |
||||||
|
} |
||||||
|
timer = new Timer(100, e -> { |
||||||
|
if (lastTabFrame != null) { |
||||||
|
var p = MouseInfo.getPointerInfo().getLocation(); |
||||||
|
SwingUtilities.convertPointFromScreen(p, lastTabFrame); |
||||||
|
var evt = new DropTargetDragEvent(lastTabFrame.getDropTarget().getDropTargetContext(), p, MOVE, MOVE); |
||||||
|
dragOver(evt); |
||||||
|
} |
||||||
|
}); |
||||||
|
timer.setRepeats(true); |
||||||
|
startTimer = new Timer(200, e -> { |
||||||
|
/* |
||||||
|
* Drag Exit can be funky. Ensure that the timer is really running. |
||||||
|
*/ |
||||||
|
if (!timer.isRunning()) { |
||||||
|
timer.start(); |
||||||
|
} |
||||||
|
}); |
||||||
|
startTimer.setRepeats(false); |
||||||
|
} |
||||||
|
|
||||||
|
@Contract("null -> null") |
||||||
|
private TabFrameUI getUI(final Component c) { |
||||||
|
if (c instanceof JTabFrame) return ((JTabFrame) c).getUI(); |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
protected JTabFrame.TabFramePosition getDropPosition(final Point p, final JTabFrame tabFrame) { |
||||||
|
return getUI(tabFrame).getDropPosition(tabFrame, p); |
||||||
|
} |
||||||
|
|
||||||
|
public void exportAsDrag(final JComponent comp, final InputEvent e, final int a) { |
||||||
|
int srcActions = getSourceActions(comp); |
||||||
|
int action = a; |
||||||
|
|
||||||
|
// only mouse events supported for drag operations
|
||||||
|
if (!(e instanceof MouseEvent) |
||||||
|
// only support known actions
|
||||||
|
|| !(action == COPY || action == MOVE || action == LINK) |
||||||
|
// only support valid source actions
|
||||||
|
|| (srcActions & action) == 0) { |
||||||
|
|
||||||
|
action = NONE; |
||||||
|
} |
||||||
|
|
||||||
|
if (action != NONE && !GraphicsEnvironment.isHeadless()) { |
||||||
|
if (recognizer == null) { |
||||||
|
recognizer = new TabbedPaneDragGestureRecognizer(new TabbedPaneDragHandler()); |
||||||
|
} |
||||||
|
recognizer.gestured(comp, (MouseEvent) e, srcActions, action); |
||||||
|
} else { |
||||||
|
exportDone(comp, null, NONE); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Called when the drag-and-drop operation has just completed. This creates a new tab identical to the one |
||||||
|
* "dragged" and places it in the destination <code>JTabbedPane</code>. |
||||||
|
* |
||||||
|
* @param c The component receiving the "drop" (the instance of |
||||||
|
* <code>JTabbedPane</code>). |
||||||
|
* @param t The data being transfered (information about the tab and the component contained by the tab). |
||||||
|
* @return Whether or not the import was successful. |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public boolean importData(final JComponent c, @NotNull final Transferable t) { |
||||||
|
boolean successful = false; |
||||||
|
if (hasTabFlavor(t.getTransferDataFlavors()) && mouseLocation != null) { |
||||||
|
try { |
||||||
|
JTabFrame tabFrame = (JTabFrame) c; |
||||||
|
var tab = getDropPosition(mouseLocation, tabFrame); |
||||||
|
Alignment a = tab.getAlignment(); |
||||||
|
int index = tab.getIndex(); |
||||||
|
TabTransferable.TabTransferData td = (TabTransferable.TabTransferData) t.getTransferData(tabFlavor); |
||||||
|
|
||||||
|
if (tabFrame == td.sourceTabFrame && td.tabAlignment == a) { |
||||||
|
if (index >= td.tabIndex) { |
||||||
|
index--; |
||||||
|
} |
||||||
|
} |
||||||
|
index++; |
||||||
|
|
||||||
|
if (tabFrame == td.sourceTabFrame && a == td.tabAlignment && index == td.tabIndex) { |
||||||
|
//Nothing to do. Just select the tab to be sure.
|
||||||
|
if (td.wasSelected) { |
||||||
|
selectTab(td.sourceTabFrame, a, index); |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
if (a == null || index < 0 || index > tabFrame.getTabCountAt(a)) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
var tabComp = td.sourceTabFrame.getTabComponentAt(td.tabAlignment, td.tabIndex); |
||||||
|
var popupComp = td.sourceTabFrame.getPopupComponentAt(td.tabAlignment, td.tabIndex); |
||||||
|
td.sourceTabFrame.removeTab(td.tabAlignment, td.tabIndex); |
||||||
|
tabFrame.insertTab((TabFramePopup) popupComp, tabComp, a, index); |
||||||
|
tabFrame.toggleTab(a, index, td.wasSelected); |
||||||
|
SwingUtilities.invokeLater(() -> td.tab.getComponent().repaint()); |
||||||
|
|
||||||
|
successful = true; |
||||||
|
var ui = getUI(c); |
||||||
|
if (ui != null) { |
||||||
|
ui.clearTargetIndicator(); |
||||||
|
} |
||||||
|
} catch (Exception e) { |
||||||
|
e.printStackTrace(); |
||||||
|
} |
||||||
|
} |
||||||
|
return successful; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Overridden to include a check for a TabData flavor. |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public boolean canImport(final JComponent c, final DataFlavor[] flavors) { |
||||||
|
return hasTabFlavor(flavors); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* We can only move tabs, we cannot copy them. |
||||||
|
* |
||||||
|
* @param c This parameter is ignored. |
||||||
|
* @return <code>TransferHandler.MOVE</code>, as we can only move tabs. |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public int getSourceActions(final JComponent c) { |
||||||
|
return MOVE; |
||||||
|
} |
||||||
|
|
||||||
|
protected boolean hasTabFlavor(final DataFlavor[] flavors) { |
||||||
|
if (tabFlavor == null) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
for (DataFlavor flavor : flavors) { |
||||||
|
if (tabFlavor.equals(flavor)) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
protected void selectTab(final JTabFrame tabbedPane, final Alignment a, final int index) { |
||||||
|
SwingUtilities.invokeLater(() -> tabbedPane.toggleTab(a, index, true)); |
||||||
|
} |
||||||
|
|
||||||
|
protected Transferable createTransferable(final JComponent c, @NotNull final DragGestureEvent dge) { |
||||||
|
JTabFrame tabFrame = (JTabFrame) c; |
||||||
|
if (tabFrame.isInTransfer()) { |
||||||
|
currentTransferable = new TabTransferable(tabFrame, tabFrame.getTransferInfo()); |
||||||
|
} else { |
||||||
|
var ind = getUI(tabFrame).getTabIndexAt(tabFrame, dge.getDragOrigin()); |
||||||
|
tabFrame.initTransfer(ind.getAlignment(), ind.getIndex()); |
||||||
|
currentTransferable = new TabTransferable(tabFrame, ind); |
||||||
|
} |
||||||
|
var ui = getUI(c); |
||||||
|
createDragImage(ui); |
||||||
|
var a = currentTransferable.transferData.tabAlignment; |
||||||
|
int index = currentTransferable.transferData.tabIndex; |
||||||
|
ui.setSourceIndicator(a, index); |
||||||
|
startTimer.start(); |
||||||
|
lastTabFrame = currentTransferable.transferData.sourceTabFrame; |
||||||
|
return currentTransferable; |
||||||
|
} |
||||||
|
|
||||||
|
protected void createDragImage(@NotNull final TabFrameUI ui) { |
||||||
|
var comp = currentTransferable.transferData.tab.getComponent(); |
||||||
|
Image tabImage = ImageUtil.scaledImageFromComponent(comp, new Rectangle(0, 0, comp.getWidth(), |
||||||
|
comp.getHeight())); |
||||||
|
int w = tabImage.getWidth(null); |
||||||
|
int h = tabImage.getHeight(null); |
||||||
|
var g = tabImage.getGraphics(); |
||||||
|
|
||||||
|
g.setColor(ui.getDragBorderColor()); |
||||||
|
|
||||||
|
int lw = 2; |
||||||
|
g.fillRect(0, 0, w, lw); |
||||||
|
g.fillRect(0, 0, lw, h); |
||||||
|
g.fillRect(w - lw, 0, lw, h); |
||||||
|
g.fillRect(0, h - lw, w, lw); |
||||||
|
g.dispose(); |
||||||
|
|
||||||
|
setDragImageOffset(new Point(w / 2, h / 2)); |
||||||
|
setDragImage(tabImage); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void dragEnter(final DropTargetDragEvent e) { |
||||||
|
timer.stop(); |
||||||
|
startTimer.stop(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void dragOver(@NotNull final DropTargetDragEvent e) { |
||||||
|
e.getDropTargetContext().getComponent().setCursor(Cursor.getDefaultCursor()); |
||||||
|
mouseLocation = e.getLocation(); |
||||||
|
|
||||||
|
Component c = e.getDropTargetContext().getComponent(); |
||||||
|
JTabFrame destTabFrame = (JTabFrame) c; |
||||||
|
|
||||||
|
var ui = getUI(destTabFrame); |
||||||
|
if (ui != null) { |
||||||
|
TabTransferable t = currentTransferable; |
||||||
|
if (t != null) { |
||||||
|
var tab = getDropPosition(mouseLocation, destTabFrame); |
||||||
|
if (tab.getAlignment() == null) { |
||||||
|
ui.clearTargetIndicator(); |
||||||
|
} else { |
||||||
|
try { |
||||||
|
var sourceTab = currentTransferable.transferData.sourceTabFrame; |
||||||
|
var sourceIndex = currentTransferable.transferData.tabIndex; |
||||||
|
var sourceAlign = currentTransferable.transferData.tabAlignment; |
||||||
|
int w = getUI(sourceTab).getTabWidth(sourceTab, sourceAlign, sourceIndex); |
||||||
|
int h = getUI(sourceTab).getTabHeight(sourceTab, sourceAlign, sourceIndex); |
||||||
|
ui.setDropSize(w, h); |
||||||
|
ui.setTargetIndicator(tab.getAlignment(), tab.getIndex()); |
||||||
|
} catch (IndexOutOfBoundsException ex) { |
||||||
|
ui.clearTargetIndicator(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
lastTabFrame = destTabFrame; |
||||||
|
startTimer.restart(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void dropActionChanged(final DropTargetDragEvent e) { |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void dragExit(@NotNull final DropTargetEvent e) { |
||||||
|
Component c = e.getDropTargetContext().getComponent(); |
||||||
|
var ui = getUI(c); |
||||||
|
if (ui != null) { |
||||||
|
ui.clearTargetIndicator(); |
||||||
|
} |
||||||
|
lastTabFrame = (JTabFrame) c; |
||||||
|
startTimer.start(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void drop(@NotNull final DropTargetDropEvent e) { |
||||||
|
Component c = e.getDropTargetContext().getComponent(); |
||||||
|
var ui = getUI(c); |
||||||
|
if (ui != null) { |
||||||
|
ui.clearTargetIndicator(); |
||||||
|
} |
||||||
|
timer.stop(); |
||||||
|
startTimer.stop(); |
||||||
|
} |
||||||
|
|
||||||
|
protected static class TabbedPaneDragGestureRecognizer extends DragGestureRecognizer { |
||||||
|
|
||||||
|
protected TabbedPaneDragGestureRecognizer(final DragGestureListener dgl) { |
||||||
|
super(DragSource.getDefaultDragSource(), null, NONE, dgl); |
||||||
|
} |
||||||
|
|
||||||
|
void gestured(final JComponent c, final MouseEvent e, final int srcActions, final int action) { |
||||||
|
setComponent(c); |
||||||
|
setSourceActions(srcActions); |
||||||
|
appendEvent(e); |
||||||
|
fireDragGestureRecognized(action, e.getPoint()); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* register this DragGestureRecognizer's Listeners with the Component |
||||||
|
*/ |
||||||
|
protected void registerListeners() { |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* unregister this DragGestureRecognizer's Listeners with the Component |
||||||
|
* <p> |
||||||
|
* subclasses must override this method |
||||||
|
*/ |
||||||
|
protected void unregisterListeners() { |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static class UIResource extends TabFrameTransferHandler { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Transferable representing a tab from a tabbed pane and its contents. |
||||||
|
*/ |
||||||
|
public class TabTransferable implements Transferable { |
||||||
|
|
||||||
|
private final TabTransferData transferData; |
||||||
|
|
||||||
|
public TabTransferable(@NotNull final JTabFrame tabFrame, @NotNull final JTabFrame.TabFramePosition ind) { |
||||||
|
transferData = new TabTransferData(tabFrame, ind.getAlignment(), ind.getIndex()); |
||||||
|
} |
||||||
|
|
||||||
|
public TabFrameTab getTab() { |
||||||
|
return transferData.tab; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public DataFlavor[] getTransferDataFlavors() { |
||||||
|
return new DataFlavor[]{tabFlavor}; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean isDataFlavorSupported(final DataFlavor flavor) { |
||||||
|
return tabFlavor.equals(flavor); |
||||||
|
} |
||||||
|
|
||||||
|
@NotNull |
||||||
|
@Override |
||||||
|
public Object getTransferData(final DataFlavor flavor) throws UnsupportedFlavorException { |
||||||
|
if (!isDataFlavorSupported(flavor)) { |
||||||
|
throw new UnsupportedFlavorException(flavor); |
||||||
|
} |
||||||
|
return transferData; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* The data remembered about the tab. |
||||||
|
*/ |
||||||
|
public class TabTransferData { |
||||||
|
|
||||||
|
private final JTabFrame sourceTabFrame; |
||||||
|
private final int tabIndex; |
||||||
|
private final Alignment tabAlignment; |
||||||
|
private final TabFrameTab tab; |
||||||
|
private final boolean wasSelected; |
||||||
|
|
||||||
|
@Contract(pure = true) |
||||||
|
public TabTransferData(@NotNull final JTabFrame tabbedPane, final Alignment tabAlignment, |
||||||
|
final int tabIndex) { |
||||||
|
this.sourceTabFrame = tabbedPane; |
||||||
|
this.tabAlignment = tabAlignment; |
||||||
|
this.tabIndex = tabIndex; |
||||||
|
this.tab = tabbedPane.getTabComponentAt(tabAlignment, tabIndex); |
||||||
|
this.wasSelected = tab.isSelected(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
protected class TabbedPaneDragHandler implements DragGestureListener, DragSourceListener { |
||||||
|
|
||||||
|
private boolean scrolls; |
||||||
|
|
||||||
|
// --- DragGestureListener methods -----------------------------------
|
||||||
|
|
||||||
|
/** |
||||||
|
* a Drag gesture has been recognized |
||||||
|
*/ |
||||||
|
public void dragGestureRecognized(@NotNull final DragGestureEvent dge) { |
||||||
|
JComponent c = (JComponent) dge.getComponent(); |
||||||
|
TabFrameTransferHandler th = (TabFrameTransferHandler) c.getTransferHandler(); |
||||||
|
Transferable t = th.createTransferable(c, dge); |
||||||
|
if (t != null) { |
||||||
|
scrolls = c.getAutoscrolls(); |
||||||
|
c.setAutoscrolls(false); |
||||||
|
try { |
||||||
|
Image im = th.getDragImage(); |
||||||
|
if (im == null) { |
||||||
|
dge.startDrag(Cursor.getDefaultCursor(), t, this); |
||||||
|
} else { |
||||||
|
dge.startDrag(Cursor.getDefaultCursor(), im, th.getDragImageOffset(), t, this); |
||||||
|
} |
||||||
|
return; |
||||||
|
} catch (RuntimeException re) { |
||||||
|
c.setAutoscrolls(scrolls); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
th.exportDone(c, t, NONE); |
||||||
|
} |
||||||
|
|
||||||
|
// --- DragSourceListener methods -----------------------------------
|
||||||
|
|
||||||
|
/** |
||||||
|
* as the hotspot enters a platform dependent drop site |
||||||
|
*/ |
||||||
|
public void dragEnter(final DragSourceDragEvent dsde) { |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* as the hotspot moves over a platform dependent drop site |
||||||
|
*/ |
||||||
|
public void dragOver(final DragSourceDragEvent dsde) { |
||||||
|
} |
||||||
|
|
||||||
|
public void dropActionChanged(final DragSourceDragEvent dsde) { |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* as the hotspot exits a platform dependent drop site |
||||||
|
*/ |
||||||
|
public void dragExit(final DragSourceEvent dsde) { |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* as the operation completes |
||||||
|
*/ |
||||||
|
public void dragDropEnd(@NotNull final DragSourceDropEvent dsde) { |
||||||
|
DragSourceContext dsc = dsde.getDragSourceContext(); |
||||||
|
JComponent c = (JComponent) dsc.getComponent(); |
||||||
|
if (dsde.getDropSuccess()) { |
||||||
|
((TabFrameTransferHandler) c.getTransferHandler()).exportDone(c, dsc.getTransferable(), |
||||||
|
dsde.getDropAction()); |
||||||
|
} else { |
||||||
|
((TabFrameTransferHandler) c.getTransferHandler()).exportDone(c, dsc.getTransferable(), NONE); |
||||||
|
} |
||||||
|
c.setAutoscrolls(scrolls); |
||||||
|
|
||||||
|
var ui = getUI(currentTransferable.transferData.sourceTabFrame); |
||||||
|
if (ui != null) { |
||||||
|
ui.clearSourceIndicator(); |
||||||
|
} |
||||||
|
if (!dsde.getDropSuccess() && currentTransferable.transferData.wasSelected) { |
||||||
|
selectTab(currentTransferable.transferData.sourceTabFrame, |
||||||
|
currentTransferable.transferData.tabAlignment, |
||||||
|
currentTransferable.transferData.tabIndex); |
||||||
|
} |
||||||
|
currentTransferable.transferData.sourceTabFrame.endTransfer(); |
||||||
|
currentTransferable = null; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,48 @@ |
|||||||
|
/* |
||||||
|
* MIT License |
||||||
|
* |
||||||
|
* Copyright (c) 2019 Jannis Weis |
||||||
|
* |
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy |
||||||
|
* of this software and associated documentation files (the "Software"), to deal |
||||||
|
* in the Software without restriction, including without limitation the rights |
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||||
|
* copies of the Software, and to permit persons to whom the Software is |
||||||
|
* furnished to do so, subject to the following conditions: |
||||||
|
* |
||||||
|
* The above copyright notice and this permission notice shall be included in all |
||||||
|
* copies or substantial portions of the Software. |
||||||
|
* |
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||||
|
* SOFTWARE. |
||||||
|
*/ |
||||||
|
package com.weis.darklaf.util; |
||||||
|
|
||||||
|
import org.jdesktop.jxlayer.JXLayer; |
||||||
|
import org.jetbrains.annotations.NotNull; |
||||||
|
import org.pbjar.jxlayer.plaf.ext.TransformUI; |
||||||
|
|
||||||
|
import javax.swing.*; |
||||||
|
import java.awt.*; |
||||||
|
|
||||||
|
public class SwingXUtilities { |
||||||
|
|
||||||
|
@NotNull |
||||||
|
public static Point convertPointToParent(final Component source, final Point p) { |
||||||
|
JXLayer layer = DarkUIUtil.getParentOfType(JXLayer.class, source); |
||||||
|
if (layer != null && layer.getUI() instanceof TransformUI) { |
||||||
|
var ui = (TransformUI) layer.getUI(); |
||||||
|
var pos = SwingUtilities.convertPoint(source, p, layer); |
||||||
|
//noinspection unchecked
|
||||||
|
var transform = ui.getPreferredTransform(layer.getSize(), layer); |
||||||
|
transform.transform(pos, pos); |
||||||
|
return pos; |
||||||
|
} |
||||||
|
return SwingUtilities.convertPoint(source, p, source.getParent()); |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue