/*
* KillRing.java - Stores deleted text
* :tabSize=4:indentSize=4:noTabs=false:
* :folding=explicit:collapseFolds=1:
*
* Copyright (C) 2003, 2005 Slava Pestov
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package org.gjt.sp.jedit.buffer;
import javax.swing.event.ListDataListener;
import java.util.Collection;
import org.gjt.sp.jedit.gui.MutableListModel;
/**
* The kill ring retains deleted text. This class is a singleton -- only one
* kill ring is used for all of jEdit. Nothing prevents plugins from making their
* own kill rings for whatever reason, though.
*/
public class KillRing implements MutableListModel
{
//{{{ getInstance() method
public static KillRing getInstance()
{
return killRing;
} //}}}
//{{{ setInstance() method
public static void setInstance(KillRing killRing)
{
KillRing.killRing = killRing;
} //}}}
//{{{ propertiesChanged() method
public void propertiesChanged(int historySize)
{
int newSize = Math.max(1, historySize);
if(ring == null)
ring = new String[newSize];
else if(newSize != ring.length)
{
String[] newRing = new String[newSize];
int newCount = Math.min(getSize(),newSize);
for(int i = 0; i < newCount; i++)
{
newRing[i] = (String)getElementAt(i);
}
ring = newRing;
count = newCount;
wrap = false;
}
if(count == ring.length)
{
count = 0;
wrap = true;
}
} //}}}
public void load() {}
public void save() {}
//{{{ reset() method
/**
* This method is made to be used by implementation of load()
* method to initialize (or reset) the killring by a loaded
* sequence of objects.
*
* Each element is converted to an element of the killring as
* followings:
* - If it is a String, it is converted as if it is a result of
* getElementAt(n).toString().
* - Otherwise, it is converted as if it is a Object which was
* obtained by getElementAt(n).
* @param source the loaded killring.
* @since jEdit 4.3pre12
*/
protected void reset(Collection<?> source)
{
String[] newRing = new String[source.size()];
int i = 0;
for(Object x: source)
{
newRing[i++] = (String)x;
}
ring = newRing;
count = 0;
wrap = true;
} //}}}
//{{{ MutableListModel implementation
@Override
public void addListDataListener(ListDataListener listener) {}
@Override
public void removeListDataListener(ListDataListener listener) {}
//{{{ getElementAt() method
@Override
public Object getElementAt(int index)
{
return ring[virtualToPhysicalIndex(index)];
} //}}}
//{{{ getSize() method
@Override
public int getSize()
{
if(wrap)
return ring.length;
else
return count;
} //}}}
//{{{ removeElement() method
@Override
public boolean removeElement(Object value)
{
for(int i = 0; i < getSize(); i++)
{
if(ring[i].equals(value))
{
remove(i);
return true;
}
}
return false;
} //}}}
//{{{ insertElementAt() method
@Override
public void insertElementAt(Object value, int index)
{
/* This is not terribly efficient, but this method is only
called by the 'Paste Deleted' dialog where the performance
is not exactly vital */
remove(index);
add((String)value);
} //}}}
//}}}
//{{{ Package-private members
//{{{ changed() method
void changed(String oldStr, String newStr)
{
int i = indexOf(oldStr);
if(i != -1)
ring[i] = newStr;
else
add(newStr);
} //}}}
//{{{ add() method
void add(String removed)
{
// we don't want duplicate entries
// in the kill ring
if(indexOf(removed) != -1)
return;
// no duplicates, check for all-whitespace string
boolean allWhitespace = true;
for(int i = 0; i < removed.length(); i++)
{
if(!Character.isWhitespace(removed.charAt(i)))
{
allWhitespace = false;
break;
}
}
if(allWhitespace)
return;
ring[count] = removed;
if(++count >= ring.length)
{
wrap = true;
count = 0;
}
} //}}}
//{{{ remove() method
void remove(int i)
{
if(wrap)
{
String[] newRing = new String[ring.length];
int newCount = 0;
for(int j = 0; j < ring.length; j++)
{
int index = virtualToPhysicalIndex(j);
if(i == index)
{
continue;
}
newRing[newCount++] = ring[index];
}
ring = newRing;
count = newCount;
wrap = false;
}
else
{
System.arraycopy(ring,i + 1,ring,i,count - i - 1);
count--;
}
} //}}}
//}}}
//{{{ Private members
private String[] ring;
private int count;
private boolean wrap;
private static KillRing killRing = new KillRing();
//{{{ virtualToPhysicalIndex() method
/**
* Since the kill ring has a wrap-around representation, we need to
* convert user-visible indices to actual indices in the array.
*/
private int virtualToPhysicalIndex(int index)
{
if(wrap)
{
if(index < count)
return count - index - 1;
else
return count + ring.length - index - 1;
}
else
return count - index - 1;
} //}}}
//{{{ indexOf() method
private int indexOf(String str)
{
int length = (wrap ? ring.length : count);
for(int i = length - 1; i >= 0; i--)
{
if(ring[i].equals(str))
{
return i;
}
}
return -1;
} //}}}
//}}}
}