mirror of
https://github.com/chylex/IntelliJ-IdeaVim.git
synced 2025-02-28 02:45:59 +01:00
cleanup
This commit is contained in:
parent
be1e1735ec
commit
ff6a7f41e9
src/com/maddyhome/idea/vim/key
@ -98,145 +98,6 @@ public class KeyParser {
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is called each time the plugin is enabled. This goes through the list of keystrokes that the plugin needs
|
||||
* such as Ctrl-A through Ctrl-Z, F1, and others. Each keystroke is checks for existing IDEA actions using the
|
||||
* keystroke and the keystroke is removed from the IDEA action. The keystroke is then added to the internal
|
||||
* VimKeyHandler action.
|
||||
*/
|
||||
/*
|
||||
public void setupShortcuts()
|
||||
{
|
||||
logger.debug("setupShortcuts");
|
||||
logger.debug("conflicts=" + conflicts);
|
||||
|
||||
keymap = KeymapManager.getInstance().getActiveKeymap();
|
||||
// Loop through each of the keystrokes the plugin needs to take ownership of.
|
||||
Iterator keys = conflicts.keySet().iterator();
|
||||
while (keys.hasNext())
|
||||
{
|
||||
KeyStroke keyStroke = (KeyStroke)keys.next();
|
||||
logger.debug("keyStroke=" + keyStroke);
|
||||
// Get all the IDEA actions that use this keystroke. Could be 0 or more.
|
||||
KeyConflict conf = (KeyConflict)conflicts.get(keyStroke);
|
||||
|
||||
if (!conf.isPluginWins()) continue;
|
||||
|
||||
setupShortcut(conf, keyStroke);
|
||||
}
|
||||
|
||||
keymap.addShortcutChangeListener(keymapListener);
|
||||
|
||||
logger.debug("setup conflicts=" + conflicts);
|
||||
}
|
||||
|
||||
private void setupShortcut(KeyConflict conf, KeyStroke keyStroke)
|
||||
{
|
||||
HashMap actions = conf.getIdeaActions();
|
||||
logger.debug("actions=" + actions);
|
||||
String[] ids = keymap.getActionIds(keyStroke);
|
||||
// For each existing IDEA action we need to remove shortcut.
|
||||
boolean added = false;
|
||||
for (int i = 0; i < ids.length; i++)
|
||||
{
|
||||
String id = ids[i];
|
||||
logger.debug("id=" + id);
|
||||
// Get the list of shortcuts for the IDEA action and find the one that matches the current keystroke
|
||||
com.intellij.openapi.actionSystem.Shortcut[] cuts = keymap.getShortcuts(id);
|
||||
for (int j = 0; j < cuts.length; j++)
|
||||
{
|
||||
com.intellij.openapi.actionSystem.Shortcut cut = cuts[j];
|
||||
logger.debug("cut=" + cut.getClass().getName());
|
||||
if (cut instanceof KeyboardShortcut)
|
||||
{
|
||||
if (((KeyboardShortcut)cut).getFirstKeyStroke().equals(keyStroke))
|
||||
{
|
||||
keymap.removeShortcut(id, cut);
|
||||
// Save off the position of the shortcut so it can be put back in the same place.
|
||||
conf.putIdeaAction(id, j);
|
||||
if (!added)
|
||||
{
|
||||
keymap.addShortcut(KEY_HANDLER, cut);
|
||||
added = true;
|
||||
}
|
||||
logger.debug("removed " + cut.getClass().getName() + " from " + id + " at " + j + " and added to VimKeyHandler");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* This is called each time the plugin is disabled. This processes all the keystrokes originally removed from their
|
||||
* original IDEA action and are put back into place.
|
||||
*/
|
||||
/*
|
||||
public void resetShortcuts()
|
||||
{
|
||||
logger.debug("resetShortcuts");
|
||||
|
||||
keymap.removeShortcutChangeListener(keymapListener);
|
||||
|
||||
// Get each of the hijacked keystrokes we stole from IDEA and put them back in their original place.
|
||||
com.intellij.openapi.actionSystem.Shortcut[] cuts = keymap.getShortcuts(KEY_HANDLER);
|
||||
for (int i = 0; i < cuts.length; i++)
|
||||
{
|
||||
com.intellij.openapi.actionSystem.Shortcut cut = cuts[i];
|
||||
if (cut instanceof KeyboardShortcut)
|
||||
{
|
||||
KeyboardShortcut ks = (KeyboardShortcut)cut;
|
||||
KeyStroke keyStroke = ks.getFirstKeyStroke();
|
||||
logger.debug("keyStroke=" + keyStroke);
|
||||
// Get the list of IDEA actions that originally had this keystroke - if any
|
||||
KeyConflict conf = (KeyConflict)conflicts.get(keyStroke);
|
||||
resetConflict(conf, cut);
|
||||
}
|
||||
}
|
||||
|
||||
logger.debug("reset conflicts=" + conflicts);
|
||||
}
|
||||
|
||||
private void resetConflict(KeyConflict conf, com.intellij.openapi.actionSystem.Shortcut cut)
|
||||
{
|
||||
// Remove the shortcut from the special plugin handler
|
||||
keymap.removeShortcut(KEY_HANDLER, cut);
|
||||
|
||||
HashMap actions = conf.getIdeaActions();
|
||||
Iterator iter = actions.keySet().iterator();
|
||||
while (iter.hasNext())
|
||||
{
|
||||
String actionId = (String)iter.next();
|
||||
int keyPos = ((Integer)actions.get(actionId)).intValue();
|
||||
logger.debug("actionId=" + actionId);
|
||||
logger.debug("keyPos=" + keyPos);
|
||||
|
||||
if (keyPos == -1) continue;
|
||||
|
||||
conf.resetIdeaAction(actionId);
|
||||
// Put back the removed shortcut. But we need to "insert" it in the same place it was so the menus
|
||||
// will show the shortcuts. Example - Undo has by default two shortcuts - Ctrl-Z and Alt-Backspace.
|
||||
// When the plugin is disabled the Undo menu shows Ctrl-Z. When the plugin is enabled we remove
|
||||
// Ctrl-Z and the Undo menu shows Alt-Backspace. When the plugin is disabled again, we need to be
|
||||
// sure Ctrl-Z is put back before Alt-Backspace or else the Undo menu will continue to show
|
||||
// Alt-Backspace even after we add back Ctrl-Z.
|
||||
com.intellij.openapi.actionSystem.Shortcut[] acuts = keymap.getShortcuts(actionId);
|
||||
logger.debug("There are " + acuts.length + " shortcuts");
|
||||
keymap.removeAllActionShortcuts(actionId);
|
||||
for (int k = 0, l = 0; k < acuts.length + 1; k++)
|
||||
{
|
||||
if (k == keyPos)
|
||||
{
|
||||
keymap.addShortcut(actionId, cut);
|
||||
}
|
||||
else
|
||||
{
|
||||
keymap.addShortcut(actionId, acuts[l++]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
public void setupActionHandler(String ideaActName, String vimActName) {
|
||||
if (logger.isDebugEnabled()) logger.debug("vimActName=" + vimActName);
|
||||
|
||||
@ -258,18 +119,9 @@ public class KeyParser {
|
||||
DelegateAction daction = (DelegateAction)vaction;
|
||||
daction.setOrigAction(iaction);
|
||||
|
||||
//Keymap keymap = KeymapManager.getInstance().getActiveKeymap();
|
||||
//com.intellij.openapi.actionSystem.Shortcut[] icuts = keymap.getShortcuts(ideaActName);
|
||||
//keymap.removeAllActionShortcuts(ideaActName);
|
||||
|
||||
amgr.unregisterAction(ideaActName);
|
||||
|
||||
amgr.registerAction(ideaActName, vaction);
|
||||
|
||||
//for (int i = 0; i < icuts.length; i++)
|
||||
//{
|
||||
// keymap.addShortcut(ideaActName, icuts[i]);
|
||||
//}
|
||||
}
|
||||
|
||||
amgr.registerAction("Orig" + ideaActName, iaction);
|
||||
@ -298,43 +150,6 @@ public class KeyParser {
|
||||
//removePossibleConflict(stroke);
|
||||
}
|
||||
|
||||
/*
|
||||
public void removePossibleConflict(KeyStroke stroke)
|
||||
{
|
||||
if (conflicts.containsKey(stroke))
|
||||
{
|
||||
conflicts.remove(stroke);
|
||||
}
|
||||
}
|
||||
public void addPossibleConflict(KeyStroke stroke, String pluginActionId)
|
||||
{
|
||||
if (!conflicts.containsKey(stroke))
|
||||
{
|
||||
KeyConflict conf = new KeyConflict(stroke);
|
||||
conf.addPluginAction(pluginActionId);
|
||||
if (ideaWins.contains(stroke))
|
||||
{
|
||||
conf.setPluginWins(false);
|
||||
}
|
||||
|
||||
conflicts.put(stroke, conf);
|
||||
|
||||
keymap = KeymapManager.getInstance().getActiveKeymap();
|
||||
String[] ids = keymap.getActionIds(stroke);
|
||||
for (int i = 0; i < ids.length; i++)
|
||||
{
|
||||
String id = ids[i];
|
||||
conf.addIdeaAction(id);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
KeyConflict conf = (KeyConflict)conflicts.get(stroke);
|
||||
conf.addPluginAction(pluginActionId);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Creates the key parser
|
||||
*/
|
||||
@ -609,336 +424,10 @@ public class KeyParser {
|
||||
return res.toString();
|
||||
}
|
||||
|
||||
/*
|
||||
public void setChoices(HashSet choices)
|
||||
{
|
||||
keymap.removeShortcutChangeListener(keymapListener);
|
||||
// Reset choices not currently in ideawins
|
||||
Iterator iter = choices.iterator();
|
||||
while (iter.hasNext())
|
||||
{
|
||||
KeyStroke keyStroke = (KeyStroke)iter.next();
|
||||
if (ideaWins.contains(keyStroke)) continue;
|
||||
|
||||
KeyConflict conf = (KeyConflict)conflicts.get(keyStroke);
|
||||
com.intellij.openapi.actionSystem.Shortcut[] cuts = keymap.getShortcuts(KEY_HANDLER);
|
||||
for (int i = 0; i < cuts.length; i++)
|
||||
{
|
||||
if (cuts[i] instanceof KeyboardShortcut)
|
||||
{
|
||||
if (((KeyboardShortcut)cuts[i]).getFirstKeyStroke().equals(keyStroke))
|
||||
{
|
||||
resetConflict(conf, cuts[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
conf.setPluginWins(false);
|
||||
}
|
||||
|
||||
// Setup ideawins not in choices anymore
|
||||
iter = ideaWins.iterator();
|
||||
while (iter.hasNext())
|
||||
{
|
||||
KeyStroke keyStroke = (KeyStroke)iter.next();
|
||||
if (choices.contains(keyStroke)) continue;
|
||||
|
||||
KeyConflict conf = (KeyConflict)conflicts.get(keyStroke);
|
||||
setupShortcut(conf, keyStroke);
|
||||
conf.setPluginWins(true);
|
||||
}
|
||||
|
||||
ideaWins = choices;
|
||||
keymap.addShortcutChangeListener(keymapListener);
|
||||
}
|
||||
|
||||
public HashSet getChoices()
|
||||
{
|
||||
return ideaWins;
|
||||
}
|
||||
|
||||
public HashMap getConflicts()
|
||||
{
|
||||
return conflicts;
|
||||
}
|
||||
|
||||
public void readData(Element element)
|
||||
{
|
||||
Element confElem = element.getChild("conflicts");
|
||||
if (confElem != null)
|
||||
{
|
||||
List keyList = confElem.getChildren("keys");
|
||||
for (int i = 0; i < keyList.size(); i++)
|
||||
{
|
||||
Element key = (Element)keyList.get(i);
|
||||
Attribute chAttr = key.getAttribute("char");
|
||||
if (chAttr != null)
|
||||
{
|
||||
char ch = key.getAttributeValue("char").charAt(0);
|
||||
ideaWins.add(KeyStroke.getKeyStroke(ch));
|
||||
}
|
||||
else
|
||||
{
|
||||
int code = Integer.parseInt(key.getAttributeValue("code"));
|
||||
int mods = Integer.parseInt(key.getAttributeValue("mods"));
|
||||
boolean rel = Boolean.getBoolean(key.getAttributeValue("release"));
|
||||
ideaWins.add(KeyStroke.getKeyStroke(code, mods, rel));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
com.intellij.openapi.actionSystem.Shortcut[] cuts = keymap.getShortcuts(KEY_HANDLER);
|
||||
Element originals = element.getChild("originals");
|
||||
if (originals != null)
|
||||
{
|
||||
List origList = originals.getChildren("original");
|
||||
for (int i = 0; i < origList.size(); i++)
|
||||
{
|
||||
Element original = (Element)origList.get(i);
|
||||
Attribute chAttr = original.getAttribute("char");
|
||||
KeyStroke key;
|
||||
if (chAttr != null)
|
||||
{
|
||||
char ch = original.getAttributeValue("char").charAt(0);
|
||||
key = KeyStroke.getKeyStroke(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
int code = Integer.parseInt(original.getAttributeValue("code"));
|
||||
int mods = Integer.parseInt(original.getAttributeValue("mods"));
|
||||
boolean rel = Boolean.getBoolean(original.getAttributeValue("release"));
|
||||
key = KeyStroke.getKeyStroke(code, mods, rel);
|
||||
}
|
||||
|
||||
for (int c = 0; c < cuts.length; c++)
|
||||
{
|
||||
if (cuts[c] instanceof KeyboardShortcut)
|
||||
{
|
||||
if (((KeyboardShortcut)cuts[c]).getFirstKeyStroke().equals(key))
|
||||
{
|
||||
keymap.removeShortcut(KEY_HANDLER, cuts[c]);
|
||||
|
||||
//ArrayList ids = new ArrayList();
|
||||
List actList = original.getChildren("action");
|
||||
for (int j = 0; j < actList.size(); j++)
|
||||
{
|
||||
Element action = (Element)actList.get(j);
|
||||
String id = action.getText();
|
||||
int pos = Integer.parseInt(action.getAttributeValue("pos"));
|
||||
if (pos == -1) continue;
|
||||
//ids.add(id);
|
||||
|
||||
com.intellij.openapi.actionSystem.Shortcut[] acuts = keymap.getShortcuts(id);
|
||||
logger.debug("There are " + acuts.length + " shortcuts");
|
||||
keymap.removeAllActionShortcuts(id);
|
||||
for (int k = 0, l = 0; k < acuts.length + 1; k++)
|
||||
{
|
||||
if (k == pos)
|
||||
{
|
||||
keymap.addShortcut(id, cuts[c]);
|
||||
}
|
||||
else
|
||||
{
|
||||
keymap.addShortcut(id, acuts[l++]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void saveData(Element element)
|
||||
{
|
||||
Element confElem = new Element("conflicts");
|
||||
Iterator iter = ideaWins.iterator();
|
||||
while (iter.hasNext())
|
||||
{
|
||||
KeyStroke key = (KeyStroke)iter.next();
|
||||
Element keyElem = new Element("keys");
|
||||
if (key.getKeyChar() != KeyEvent.CHAR_UNDEFINED)
|
||||
{
|
||||
keyElem.setAttribute("char", Character.toString(key.getKeyChar()));
|
||||
}
|
||||
else
|
||||
{
|
||||
keyElem.setAttribute("code", Integer.toString(key.getKeyCode()));
|
||||
keyElem.setAttribute("mods", Integer.toString(key.getModifiers()));
|
||||
keyElem.setAttribute("release", Boolean.toString(key.isOnKeyRelease()));
|
||||
}
|
||||
confElem.addContent(keyElem);
|
||||
}
|
||||
element.addContent(confElem);
|
||||
|
||||
Element originals = new Element("originals");
|
||||
iter = conflicts.keySet().iterator();
|
||||
while (iter.hasNext())
|
||||
{
|
||||
KeyStroke keyStroke = (KeyStroke)iter.next();
|
||||
KeyConflict conf = (KeyConflict)conflicts.get(keyStroke);
|
||||
if (conf.hasConflict())
|
||||
{
|
||||
Element original = new Element("original");
|
||||
if (keyStroke.getKeyChar() != KeyEvent.CHAR_UNDEFINED)
|
||||
{
|
||||
original.setAttribute("char", Character.toString(keyStroke.getKeyChar()));
|
||||
}
|
||||
else
|
||||
{
|
||||
original.setAttribute("code", Integer.toString(keyStroke.getKeyCode()));
|
||||
original.setAttribute("mods", Integer.toString(keyStroke.getModifiers()));
|
||||
original.setAttribute("release", Boolean.toString(keyStroke.isOnKeyRelease()));
|
||||
}
|
||||
|
||||
HashMap acts = conf.getIdeaActions();
|
||||
Iterator aIter = acts.keySet().iterator();
|
||||
while (aIter.hasNext())
|
||||
{
|
||||
String name = (String)aIter.next();
|
||||
if (name.equals(KEY_HANDLER)) continue;
|
||||
|
||||
Integer pos = (Integer)acts.get(name);
|
||||
Element action = new Element("action");
|
||||
action.setText(name);
|
||||
action.setAttribute("pos", pos.toString());
|
||||
original.addContent(action);
|
||||
}
|
||||
originals.addContent(original);
|
||||
}
|
||||
}
|
||||
|
||||
element.addContent(originals);
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Unfortunately this isn't called when the user makes a shortcut change using the Keymap Settings dialog.
|
||||
* This "bug" has been reported against IDEA 5.0.2 (3542).
|
||||
* Due to this bug many changes a user makes with the settings dialog will basically be lost.
|
||||
*/
|
||||
private class KeyChangeListener implements Keymap.Listener {
|
||||
// Possible changes: (1 or more)
|
||||
// 1) User removed a shortcut that was never a conflict.
|
||||
// Nothing to do or even check on this - who cares.
|
||||
//
|
||||
// 2) User removed a shortcut that was a conflict.
|
||||
// Remove the action from the conflict list for the key.
|
||||
//
|
||||
// 3) User put back a shortcut removed by this plugin on same action.
|
||||
// This only happens if previous setting was for plugin to win so change key to 'idea wins' and reset
|
||||
// conflict. Shortcut will already be on idea action. May or may not still be on 'VimKeyHandler'. Put
|
||||
// shortcut back on any other idea actions that used it too.
|
||||
//
|
||||
// 4) User added a shortcut to a different action and shortcut was a conflict.
|
||||
// If key is currently 'idea wins' then simply add shortcut to key's list of conflicts.
|
||||
// If key is currently 'plugin wins' then put shortcut reset conflict. Change key to 'idea wins'.
|
||||
// Shortcut will already be on new idea action. May or may not still be on 'VimKeyHandler'. Add
|
||||
// action to key's list of conflicts.
|
||||
//
|
||||
// 5) User added a shortcut to a different action and shortcut is now a conflict.
|
||||
// No prior idea actions used this key. Add action to key's list of conflicts. Mark key as 'idea wins'.
|
||||
// Action will already have shortcut. Need to remove shortcut from 'VimKeyHandler' (may already be gone).
|
||||
//
|
||||
// 6) User added a shortcut to a different action and shortcut still isn't a conflict.
|
||||
// Nothing to do.
|
||||
public void onShortcutChanged(String actionId) {
|
||||
/* TODO - this is untested code due to the bug.
|
||||
// Get the newly updated list of shortcuts for this action
|
||||
com.intellij.openapi.actionSystem.Shortcut[] cuts = keymap.getShortcuts(actionId);
|
||||
|
||||
// Check the current master conflict list to see if there is a conflict for a key no longer associated
|
||||
// with this action.
|
||||
Iterator iter = conflicts.keySet().iterator();
|
||||
while (iter.hasNext())
|
||||
{
|
||||
KeyStroke key = (KeyStroke)iter.next();
|
||||
KeyConflict conf = (KeyConflict)conflicts.get(key);
|
||||
// We found a key with this action
|
||||
if (conf.getIdeaActions().containsKey(actionId))
|
||||
{
|
||||
boolean found = false;
|
||||
// Now check to see if any of the new shortcuts are already known about.
|
||||
for (int i = 0; i < cuts.length; i++)
|
||||
{
|
||||
com.intellij.openapi.actionSystem.Shortcut cut = cuts[i];
|
||||
if (cut instanceof KeyboardShortcut)
|
||||
{
|
||||
// OK - we already knew about this shortcut
|
||||
if (((KeyboardShortcut)cut).getFirstKeyStroke().equals(key))
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// This key isn't listed anymore. The user must have removed it as part of this change
|
||||
// so we need to remove it from our conflict list. That should be all. What ever other
|
||||
// conflict may exist for this key can stay as is.
|
||||
if (!found)
|
||||
{
|
||||
conf.removeIdeaAction(actionId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now we need to go through each of the shortcuts in the updated list.
|
||||
for (int i = 0; i < cuts.length; i++)
|
||||
{
|
||||
com.intellij.openapi.actionSystem.Shortcut cut = cuts[i];
|
||||
if (cut instanceof KeyboardShortcut)
|
||||
{
|
||||
KeyStroke key = ((KeyboardShortcut)cut).getFirstKeyStroke();
|
||||
KeyConflict conf = (KeyConflict)conflicts.get(key);
|
||||
// This is a new conflict that didn't exist for this action before
|
||||
if (!conf.getIdeaActions().containsKey(actionId))
|
||||
{
|
||||
// Assume that the user wants this new shortcut to override what ever it normally does
|
||||
// in the plugin (otherwise they wouldn't have added it to the Idea action).
|
||||
// First thing is to reset the shortcut by restoring the shortcut to any and all Idea
|
||||
// actions we took it away from.
|
||||
if (conf.isPluginWins())
|
||||
{
|
||||
resetConflict(conf, cut);
|
||||
}
|
||||
|
||||
// Add this as a new action for the key and mark it as Idea winning over the plugin.
|
||||
conf.addIdeaAction(actionId);
|
||||
conf.setPluginWins(false);
|
||||
ideaWins.add(key);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (conf.isPluginWins())
|
||||
{
|
||||
// The shortcut existed before but was marked as 'plugin wins'. If we are here the
|
||||
// user must have used the Keymap settings to put the shortcut back to the idea
|
||||
// action. So lets mark it as 'idea wins' and reset the shortcut on the actions.
|
||||
conf.resetIdeaAction(actionId);
|
||||
resetConflict(conf, cut);
|
||||
conf.setPluginWins(false);
|
||||
ideaWins.add(key);
|
||||
}
|
||||
else
|
||||
{
|
||||
// The shortcut existed before and was already setup as 'idea wins' so this can't be
|
||||
// a changed shortcut - nothing to do.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
private HashMap<Integer, RootNode> keyRoots = new HashMap<Integer, RootNode>();
|
||||
//private HashMap conflicts = new HashMap();
|
||||
//private HashSet ideaWins = new HashSet();
|
||||
//private Keymap keymap;
|
||||
//private KeyChangeListener keymapListener = new KeyChangeListener();
|
||||
|
||||
private static KeyParser instance;
|
||||
|
||||
private static Logger logger = Logger.getInstance(KeyParser.class.getName());
|
||||
//private static final String KEY_HANDLER = "VimKeyHandler";
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ public class RootNode extends ParentNode {
|
||||
res.append("children=[");
|
||||
int cnt = 0;
|
||||
for (Object key : children.keySet()) {
|
||||
Node node = (Node)children.get(key);
|
||||
Node node = children.get(key);
|
||||
if (cnt > 0) {
|
||||
res.append(",");
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user