refactor Emoticon plugin

This commit is contained in:
Sergey Ponomarev
2026-07-26 16:00:20 +03:00
parent 89a904c008
commit ad4c86c036
7 changed files with 179 additions and 241 deletions

View File

@ -63,11 +63,8 @@ public class ButtonFactory {
public RolloverButton createEmoticonButton() {
final EmoticonManager emoticonManager = EmoticonManager.getInstance();
final String activeEmoticonSetName = emoticonManager.getActiveEmoticonSetName();
final Emoticon smileEmoticon = emoticonManager.getEmoticon(activeEmoticonSetName, ":)");
if (smileEmoticon != null) {
URL emotionURL = emoticonManager.getEmoticonURL(smileEmoticon);
ImageIcon icon = new ImageIcon(emotionURL);
ImageIcon icon = emoticonManager.getEmoticonImage(":)");
if (icon != null) {
return new RolloverButton(icon);
}
return new RolloverButton(":)");

View File

@ -52,6 +52,7 @@ import java.awt.Insets;
import java.awt.Window;
import java.io.File;
import java.util.Objects;
import java.util.Set;
import static java.awt.GridBagConstraints.BOTH;
import static java.awt.GridBagConstraints.CENTER;
@ -101,7 +102,7 @@ public class ThemePanel extends JPanel {
private final JComboBox<String> _showReconnectBox;
private final JScrollPane emoticonScrollPane;
private JPanel emoticonsPanel;
private EmoticonPanel emoticonsPanel;
private final LocalPreferences pref = SettingsManager.getLocalPreferences();
@ -185,7 +186,7 @@ public class ThemePanel extends JPanel {
TranscriptWindow emoticonPreviewTranscript = new TranscriptWindow();
emoticonPreviewTranscript.setForceEmoticons(true);
String[] sizeChoices = {"16", "24", "32", "48", "96", "120"};
String[] sizeChoices = {"16", "24", "32", "48", "64"};
avatarSizeField = new JComboBox<>(sizeChoices);
String[] r = {
@ -277,10 +278,8 @@ public class ThemePanel extends JPanel {
hideInTaskbar.setSelected(pref.isHideInTaskbar());
final EmoticonManager emoticonManager = EmoticonManager.getInstance();
if (emoticonManager.getEmoticonPacks() != null) {
for (String pack : emoticonManager.getEmoticonPacks()) {
emoticonBox.addItem(pack);
}
for (String pack : emoticonManager.getEmoticonPacks()) {
emoticonBox.addItem(pack);
}
final String activePack = pref.getEmoticonPack();
@ -323,7 +322,8 @@ public class ThemePanel extends JPanel {
*/
protected void showSelectedEmoticon() {
EmoticonManager emoticonManager = EmoticonManager.getInstance();
int i = emoticonManager.getActiveEmoticonSet().size();
Set<Emoticon> activeEmoticonSet = emoticonManager.getActiveEmoticonSet();
int i = activeEmoticonSet.size();
if (i == 0) {
emoticonsPanel = new EmoticonPanel(1);
JLabel label = new JLabel(SparkRes.getImageIcon(SparkRes.Icon.SMALL_DELETE));
@ -333,13 +333,13 @@ public class ThemePanel extends JPanel {
} else {
emoticonsPanel = new EmoticonPanel(10);
}
for (Emoticon emoticon : emoticonManager.getActiveEmoticonSet()) {
for (Emoticon emoticon : activeEmoticonSet) {
ImageIcon ico = new ImageIcon(emoticonManager.getEmoticonURL(emoticon));
JLabel label = new JLabel(ico);
emoticonsPanel.add(label);
}
int rows = Math.min(((EmoticonPanel) emoticonsPanel).getNumRows() * 45, 300);
int rows = Math.min(emoticonsPanel.getNumRows() * 45, 300);
emoticonScrollPane.setPreferredSize(new Dimension(300, rows));
emoticonScrollPane.setViewportView(emoticonsPanel);
this.revalidate();

View File

@ -270,7 +270,11 @@ public class URLFileSystem {
*/
public static String getName(URL url) {
final String fileName = getFileName(url);
final int firstDot = fileName.lastIndexOf('.');
return getName(fileName);
}
public static String getName(String fileName) {
int firstDot = fileName.lastIndexOf('.');
return firstDot > 0 ? fileName.substring(0, firstDot) : fileName;
}

View File

@ -24,62 +24,45 @@ import java.io.File;
* @author Derek DeMoro
*/
public class Emoticon {
private String imageName;
private String emoticonName;
private final String imageName;
private final String emoticonName;
private final File emoticonDirectory;
private final List<String> equivalants;
private final List<String> equivalents;
/**
* Creates a single Emoticon entry.
*
* @param nameOfImage the name of the image that represents this emoticon (ex. smile.gif)
* @param emoticonName the name of this emoticon
* @param equivalants all string representations of this emoticon.
* @param equivalents all string representations of this emoticon.
* @param emoticonDirectory Directory that contains emoticons.
*/
public Emoticon(String nameOfImage, String emoticonName, List<String> equivalants, File emoticonDirectory) {
public Emoticon(String nameOfImage, String emoticonName, List<String> equivalents, File emoticonDirectory) {
this.imageName = nameOfImage;
this.emoticonName = emoticonName;
this.equivalants = equivalants;
this.equivalents = equivalents;
this.emoticonDirectory = emoticonDirectory;
}
/**
* Return the name of the image.
*
* @return image name.
*/
public String getImageName() {
return imageName;
}
public void setImageName(String imageName) {
this.imageName = imageName;
}
/**
* Returns the name of this emoticon.
*
* @return name of emoticon.
*/
public String getEmoticonName() {
return emoticonName;
}
public void setEmoticonName(String emoticonName) {
this.emoticonName = emoticonName;
}
/**
* Returns all text equivilants of this emoticon.
*
* @return list of all text equivilants.
* Returns all text equivalents of this emoticon.
*/
public List<String> getEquivalants() {
return equivalants;
public List<String> getEquivalents() {
return equivalents;
}
public File getEmoticonDirectory(){

View File

@ -46,6 +46,7 @@ import java.util.jar.JarFile;
import java.util.zip.ZipFile;
import static java.util.Arrays.asList;
import static org.apache.commons.lang3.Strings.CI;
/**
* Responsible for the handling of all Emoticon packs. Using the
@ -61,22 +62,22 @@ public class EmoticonManager {
// Mapped by pack name, then by 'equivalent' key.
private final Map<String, Map<String, Emoticon>> emoticonMap = new HashMap<>();
private final Map<String, ImageIcon> imageMap = new HashMap<>();
private Map<String, Emoticon> activeEmoticonMap;
/**
* The root emoticon directory.
*/
public File EMOTICON_DIRECTORY;
private File EMOTICON_DIRECTORY;
private final LocalPreferences pref = SettingsManager.getLocalPreferences();
public static EmoticonManager getInstance() {
synchronized (LOCK) {
if (null == singleton) {
EmoticonManager controller = new EmoticonManager();
singleton = controller;
return controller;
}
if (singleton != null) {
return singleton;
}
synchronized (LOCK) {
singleton = new EmoticonManager();
return singleton;
}
return singleton;
}
private EmoticonManager() {
@ -118,10 +119,10 @@ public class EmoticonManager {
// Check if File is Zip-File
int endIndex = file.getName().indexOf(".zip");
if (endIndex > 0) {
String unzipURL = file.getName().substring(0, endIndex);
File unzipFile = new File(profileEmoticonsFolder, unzipURL);
if (!unzipFile.exists() || !checkIfSameFile(file, newFile)) {
// Copy over and expand :)
String unzippedFolderName = file.getName().substring(0, endIndex);
File unzippedFolder = new File(profileEmoticonsFolder, unzippedFolderName);
if (!unzippedFolder.exists() || !checkIfSameFile(file, newFile)) {
Log.debug("Copying " + file.getName() + " to " + profileEmoticonsFolder + " and unpack");
URLFileSystem.copy(file.toURI().toURL(), newFile);
expandNewPack(newFile, profileEmoticonsFolder);
}
@ -175,22 +176,15 @@ public class EmoticonManager {
/**
* Returns the active emoticon set within Spark.
*/
public Collection<Emoticon> getActiveEmoticonSet() {
public Set<Emoticon> getActiveEmoticonSet() {
String emoticonPack = pref.getEmoticonPack();
// If EmoticonPack is set
//When no emoticon set is available, return an empty list
if (emoticonPack != null) {
Map<String, Emoticon> emoticons = emoticonMap.get(emoticonPack);
return emoticons != null ? new LinkedHashSet<>(emoticons.values()) : List.of();
// When no emoticon set is available, return an empty list
return emoticons != null ? new LinkedHashSet<>(emoticons.values()) : Set.of();
}
return List.of();
}
/**
* Returns the name of the active emoticon set.
*/
public String getActiveEmoticonSetName() {
return pref.getEmoticonPack();
return Set.of();
}
/**
@ -201,6 +195,7 @@ public class EmoticonManager {
public void setActivePack(String pack) {
pref.setEmoticonPack(pack);
imageMap.clear();
activeEmoticonMap = null;
}
/**
@ -213,21 +208,18 @@ public class EmoticonManager {
if (!containsEmoticonPList(pack)) {
return null;
}
String name;
// Copy to the emoticon area
try {
File dst = new File(EMOTICON_DIRECTORY, pack.getName());
URLFileSystem.copy(pack.toURI().toURL(), dst);
File rootDirectory = unzipPack(pack, EMOTICON_DIRECTORY);
name = URLFileSystem.getName(rootDirectory.toURI().toURL());
String name = URLFileSystem.getName(rootDirectory.toURI().toURL());
addEmoticonPack(name);
} catch (IOException e) {
Log.error(e);
return name;
} catch (Exception e) {
Log.error("Unable to install emoticon pack: " + pack.getAbsolutePath(), e);
return null;
}
return name;
}
/**
@ -240,11 +232,8 @@ public class EmoticonManager {
if (!emoticonSet.exists()) {
emoticonSet = new File(EMOTICON_DIRECTORY, packName + ".AdiumEmoticonset");
}
if (!emoticonSet.exists()) {
emoticonSet = new File(EMOTICON_DIRECTORY, "Default.adiumemoticonset");
packName = "Default";
setActivePack("Default");
Log.error("The emoticons file not found in " + emoticonSet.getAbsolutePath());
}
final File plist = new File(emoticonSet, "Emoticons.plist");
@ -252,7 +241,6 @@ public class EmoticonManager {
Log.error("Emoticons.plist not found in " + emoticonSet.getAbsolutePath());
return;
}
Map<String, Emoticon> emoticons = new LinkedHashMap<>();
// Create SaxReader and set to non-validating parser.
// This will allow for non-http problems to not break spark :)
@ -280,6 +268,7 @@ public class EmoticonManager {
return;
}
Map<String, Emoticon> emoticons = new LinkedHashMap<>();
Node root = emoticonFile.selectSingleNode("/plist/dict/dict");
List<Node> keyList = root.selectNodes("key");
List<Node> dictonaryList = root.selectNodes("dict");
@ -295,7 +284,7 @@ public class EmoticonManager {
equivs.add(equivalent.getText());
}
final Emoticon emoticon = new Emoticon(key, name, equivs, emoticonSet);
for (String equivalent : emoticon.getEquivalants()) {
for (String equivalent : emoticon.getEquivalents()) {
emoticons.put(equivalent, emoticon);
}
}
@ -318,21 +307,6 @@ public class EmoticonManager {
return null;
}
/**
* Retrieves the associated key emoticon.
*
* @param packName the name of the Archive Pack File.
* @param key the key.
* @return the emoticon.
*/
public Emoticon getEmoticon(String packName, String key) {
final Map<String, Emoticon> emoticons = emoticonMap.get(packName);
if (emoticons == null) {
return null;
}
return emoticons.get(key);
}
/**
* Returns the <code>Emoticon</code> associated with the given key. Note:
* This gets the emoticon from the active emoticon pack.
@ -341,7 +315,14 @@ public class EmoticonManager {
* @return the Emoticon found. If no emoticon is found, null is returned.
*/
public Emoticon getEmoticon(String key) {
return getEmoticon(getActiveEmoticonSetName(), key);
if (activeEmoticonMap == null) {
String packName = pref.getEmoticonPack();
activeEmoticonMap = emoticonMap.get(packName);
if (activeEmoticonMap == null) {
activeEmoticonMap = Map.of();
}
}
return activeEmoticonMap.get(key);
}
/**
@ -352,39 +333,31 @@ public class EmoticonManager {
*/
public ImageIcon getEmoticonImage(String key) {
final Emoticon emoticon = getEmoticon(key);
if (emoticon != null) {
ImageIcon icon = imageMap.get(key);
if (icon == null) {
URL url = getEmoticonURL(emoticon);
icon = new ImageIcon(url);
imageMap.put(key, icon);
}
return imageMap.get(key);
if (emoticon == null) {
return null;
}
return null;
ImageIcon icon = imageMap.computeIfAbsent(key, it -> {
URL url = getEmoticonURL(emoticon);
return new ImageIcon(url);
});
return icon;
}
/**
* Returns a list of all available emoticon packs.
*
* @return Collection of Emoticon Pack names.
*/
public Collection<String> getEmoticonPacks() {
final List<String> emoticonList = new ArrayList<>();
File[] dirs = EMOTICON_DIRECTORY.listFiles(File::isDirectory);
// If no emoticons are available
if (dirs == null) {
return null;
return List.of();
}
List<String> emoticonList = new ArrayList<>(dirs.length);
for (File file : dirs) {
if (file.getName().toLowerCase().endsWith("adiumemoticonset")) {
try {
String name = URLFileSystem.getName(file.toURI().toURL());
name = name.replace("adiumemoticonset", "");
name = name.replace("AdiumEmoticonset", "");
emoticonList.add(name);
} catch (MalformedURLException ignored) {
}
String fileName = file.getName();
if (CI.endsWith(fileName, ".adiumemoticonset")) {
String name = URLFileSystem.getName(fileName);
emoticonList.add(name);
}
}
return emoticonList;
@ -397,15 +370,8 @@ public class EmoticonManager {
* @param dist Dist file.
*/
private void expandNewPack(File file, File dist) {
URL url;
try {
url = file.toURI().toURL();
} catch (MalformedURLException ignored) {
return;
}
String name = URLFileSystem.getName(url);
String name = URLFileSystem.getName(file.getName());
File directory = new File(dist, name);
// Unzip contents into directory
unzipPack(file, directory.getParentFile());
}
@ -489,7 +455,7 @@ public class EmoticonManager {
}
/**
* Deletes Emoticons in pathToSearch that have a different md5-hash than its correspondant in install\spark\xtra\emoticons
* Deletes Emoticons in pathToSearch that have a different md5-hash than its correspondent in install\spark\xtra\emoticons
*/
private void deleteOldEmoticons(File pathToSearch) {
final File installPath = new File(Spark.getXtraDirectory(), "emoticons");
@ -526,6 +492,7 @@ public class EmoticonManager {
}
private void uninstall(File emoticonDir) {
Log.warning("Uninstall emoticons pack: " + emoticonDir.getAbsolutePath());
try {
Files.walkFileTree(emoticonDir.toPath(), new SimpleFileVisitor<>() {
@Override

View File

@ -43,11 +43,10 @@ import org.jivesoftware.sparkimpl.settings.local.SettingsManager;
* @author Derek DeMoro
*/
public class EmoticonPlugin implements Plugin, ChatRoomListener {
private ChatManager chatManager;
@Override
public void initialize() {
chatManager = SparkManager.getChatManager();
@Override
public void initialize() {
ChatManager chatManager = SparkManager.getChatManager();
// Listen for rooms opening to add emoticon picker
chatManager.addChatRoomListener(this);
// Add Preferences
@ -55,66 +54,60 @@ public class EmoticonPlugin implements Plugin, ChatRoomListener {
}
@Override
public void chatRoomOpened(final ChatRoom room) {
// Check to see if emoticons are enabled.
if (!SettingsManager.getLocalPreferences().areEmoticonsEnabled()) {
return;
}
// Add Emoticon button
final RolloverButton emoticonPicker = UIComponentRegistry.getButtonFactory().createEmoticonButton();
room.addEditorComponent(emoticonPicker);
emoticonPicker.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
// Show popup
final JPopupMenu popup = new JPopupMenu();
EmoticonUI emoticonUI = new EmoticonUI();
emoticonUI
.setEmoticonPickListener( emoticon -> {
try {
popup.setVisible(false);
final ChatInputEditor editor = room.getChatInputEditor();
String currentText = editor.getText();
if (currentText.isEmpty() || currentText.endsWith(" ")) {
room.getChatInputEditor().insertText(emoticon + " ");
} else {
room.getChatInputEditor()
.insertText(" " + emoticon + " ");
}
room.getChatInputEditor().requestFocus();
} catch (BadLocationException e1) {
Log.error(e1);
}
} );
popup.add(emoticonUI);
int actualX = e.getX()+10;
int actualY = e.getY();
final Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
final ChatContainer chat = SparkManager.getChatManager().getChatContainer();
// if height Spark Chat Windows > height 0.9* screenSize we should put emoticon window above
if(chat.getHeight() > screenSize.getHeight()*0.90){
actualY = e.getY()-100;
public void chatRoomOpened(ChatRoom room) {
// Check to see if emoticons are enabled.
if (!SettingsManager.getLocalPreferences().areEmoticonsEnabled()) {
return;
}
// Add Emoticon button
RolloverButton emoticonPicker = UIComponentRegistry.getButtonFactory().createEmoticonButton();
room.addEditorComponent(emoticonPicker);
emoticonPicker.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
// Show popup
JPopupMenu popup = new JPopupMenu();
EmoticonUI emoticonUI = new EmoticonUI();
emoticonUI.setEmoticonPickListener(emoticon -> {
popup.setVisible(false);
ChatInputEditor editor = room.getChatInputEditor();
String currentText = editor.getText();
String emoticonText = currentText.isEmpty() || currentText.endsWith(" ") ? emoticon + " " : " " + emoticon + " ";
try {
room.getChatInputEditor().insertText(emoticonText);
} catch (BadLocationException e1) {
Log.error(e1);
}
popup.show(emoticonPicker, actualX, actualY);
}
});
room.getChatInputEditor().requestFocus();
});
room.addClosingListener( () -> room.removeEditorComponent(emoticonPicker) );
}
popup.add(emoticonUI);
int actualX = e.getX() + 10;
int actualY = e.getY();
final Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
final ChatContainer chat = SparkManager.getChatManager().getChatContainer();
// if height Spark Chat Windows > height 0.9*screenSize, we should put emoticon window above
if (chat.getHeight() > screenSize.getHeight() * 0.90) {
actualY = e.getY() - 100;
}
popup.show(emoticonPicker, actualX, actualY);
}
});
@Override
public void shutdown() {
}
room.addClosingListener(() -> room.removeEditorComponent(emoticonPicker));
}
@Override
public boolean canShutDown() {
return false;
}
@Override
public void shutdown() {
}
@Override
public void uninstall() {
}
@Override
public boolean canShutDown() {
return false;
}
@Override
public void uninstall() {
}
}

View File

@ -25,7 +25,7 @@ import java.awt.Container;
import java.awt.Dimension;
import java.net.URL;
import java.util.Collection;
import java.util.Set;
import javax.swing.ImageIcon;
import javax.swing.JPanel;
@ -33,66 +33,60 @@ import javax.swing.JPanel;
import javax.swing.ScrollPaneConstants;
public class EmoticonUI extends JPanel {
private EmoticonPickListener listener;
private EmoticonPickListener listener;
public EmoticonUI() {
setBackground(Color.white);
final EmoticonManager manager = EmoticonManager.getInstance();
Collection<Emoticon> emoticons = manager.getActiveEmoticonSet();
if (emoticons != null) {
int no = emoticons.size();
// Emoticons per row
int cntInRow = 6;
// Count rows of Emoticons
int rows = no / cntInRow + ((no % cntInRow == 0) ? 0 : 1);
Container gridContainer = new Container();
GridLayout grid = new GridLayout(0, cntInRow);
JScrollPane scrollPane = new JScrollPane(gridContainer);
scrollPane.getViewport().setBackground(Color.WHITE);
scrollPane.setBorder(BorderFactory.createEmptyBorder());
gridContainer.setLayout(grid);
// Show only vertical scrollbar if it needed
scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
int scrollBarWidth = scrollPane.getVerticalScrollBar().getPreferredSize().width;
// Add ScrollPane to Panel
add(scrollPane);
// setIgnoreRepaint(true);
// Add Emoticons
for (Emoticon emoticon : emoticons) {
final String text = emoticon.getEquivalants().get(0);
String name = manager.getActiveEmoticonSetName();
final Emoticon smileEmoticon = manager.getEmoticon(name, text);
URL smileURL = manager.getEmoticonURL(smileEmoticon);
// Add Emoticon button
ImageIcon icon = new ImageIcon(smileURL);
public EmoticonUI() {
setBackground(Color.white);
final EmoticonManager manager = EmoticonManager.getInstance();
Set<Emoticon> emoticons = manager.getActiveEmoticonSet();
int no = emoticons.size();
// Emoticons per row
int cntInRow = 6;
// Count rows of Emoticons
int rows = no / cntInRow + ((no % cntInRow == 0) ? 0 : 1);
Container gridContainer = new Container();
GridLayout grid = new GridLayout(0, cntInRow);
JScrollPane scrollPane = new JScrollPane(gridContainer);
scrollPane.getViewport().setBackground(Color.WHITE);
scrollPane.setBorder(BorderFactory.createEmptyBorder());
gridContainer.setLayout(grid);
// Show only vertical scrollbar if it needed
scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
int scrollBarWidth = scrollPane.getVerticalScrollBar().getPreferredSize().width;
// Add ScrollPane to Panel
add(scrollPane);
// Add Emoticons
for (Emoticon emoticon : emoticons) {
String text = emoticon.getEquivalents().get(0);
URL smileURL = manager.getEmoticonURL(emoticon);
// Add Emoticon button
ImageIcon icon = new ImageIcon(smileURL);
RolloverButton emotButton = new RolloverButton();
emotButton.setIcon(icon);
emotButton.addActionListener( e -> listener.emoticonPicked(text) );
RolloverButton emotButton = new RolloverButton();
emotButton.setIcon(icon);
emotButton.addActionListener(e -> listener.emoticonPicked(text));
emotButton.setToolTipText(emoticon.getEmoticonName() + " " + text);
gridContainer.add(emotButton);
}
// Set up parameters of vertical scrollbar
scrollPane.getVerticalScrollBar().setMaximum(rows);
scrollPane.getVerticalScrollBar().setUnitIncrement(55);
gridContainer.add(emotButton);
}
// Set up parameters of vertical scrollbar
scrollPane.getVerticalScrollBar().setMaximum(rows);
scrollPane.getVerticalScrollBar().setUnitIncrement(55);
// Change width of ScrollPane if it needed
Dimension containerPreferredSize = gridContainer.getPreferredSize();
if (containerPreferredSize.getHeight() > containerPreferredSize.getWidth()) {
int width = (int) containerPreferredSize.getWidth() + 2 * scrollBarWidth;
int height = (int) containerPreferredSize.getWidth() * 2 / 3;
scrollPane.setPreferredSize(new Dimension(width, height));
}
}
// Change width of ScrollPane if it needed
if (gridContainer.getPreferredSize().getHeight() > gridContainer.getPreferredSize().getWidth()) {
scrollPane.setPreferredSize(new Dimension(
(int) gridContainer.getPreferredSize().getWidth() + 2 * scrollBarWidth,
(int) gridContainer.getPreferredSize().getWidth() * 2 / 3
));
}
}
}
public void setEmoticonPickListener(EmoticonPickListener listener) {
this.listener = listener;
}
public void setEmoticonPickListener(EmoticonPickListener listener) {
this.listener = listener;
}
public interface EmoticonPickListener {
void emoticonPicked(String emoticon);
}
public interface EmoticonPickListener {
void emoticonPicked(String emoticon);
}
}