package GUI;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import javax.imageio.ImageIO;
import javax.swing.JPanel;
import javax.swing.event.MouseInputListener;
import Communication.Status;
import Control.Contact;
import Control.Group;
import Control.Settings;
import Control.Interfaces.ListElement;
import GUI.Interfaces.ContactListListener;
/**
* Represents the buddy list of the client according to rules of the {@link Settings}.
* References therefore on information of all {@link ListElement}s, being either {@link Contact}s or the
* name of a {@link Group}.
*
* <p>Informs {@link ContactListListener} on the need of a {@link ScrollBar} to fully display all
* {@link ListElement}s as well as on other things.</p>
*
* <p>Needs to be notified on changes e.g. on the status of somebody in the contact list.</p>
*/
public class ContactList extends JPanel implements MouseInputListener{
/**
* Lists all elements that can be displayed
*/
private ListElement[] elementsInList;
/**
* Lists all elements to be displayed in the determined order
*/
private ArrayList<ListElement> displayedElements = new ArrayList<ListElement>();
private ArrayList<ContactListListener> contactListListener = new ArrayList<ContactListListener>();
/**
* The {@link Color} to mark an element with
*/
private Color markColor;
/**
* The {@link Color} to highlight an element with
*/
private Color hightlightColor;
/**
* The {@link Settings} that define the appearance of the list
*/
private Settings settings;
private Image[] symbols = new Image[4];
private int virtualAxis = 0;
private final int ELEMENT_HEIGHT = 20;
private int offset = 0;
private int firstElementIndex = 0;
private int markedElementIndex = -1;
private int highlightedElementIndex = -1;
/**
* Indicates whether or not a {@link ScrollBar} is needed to fully display the list. Also indicates
* the height of the complete list. If the {@link ScrollBar} isn't needed the value has to be
* <code>-1</code>.
*/
private int scrollBarHeight = -1;
public ContactList(Settings settings, ListElement[] elements){
this.elementsInList = elements;
this.settings = settings;
addMouseListener(this);
addMouseMotionListener(this);
hightlightColor = new Color(229,223,241);
markColor = new Color(173,171,231);
try {
BufferedImage[] symbols = {
ImageIO.read(new File("Ressources/online.png")),
ImageIO.read(new File("Ressources/away.png")),
ImageIO.read(new File("Ressources/invisible.png")),
ImageIO.read(new File("Ressources/offline.png"))
};
this.symbols[0] = symbols[0].getScaledInstance(15, 15, Image.SCALE_SMOOTH);
this.symbols[1] = symbols[1].getScaledInstance(15, 15, Image.SCALE_SMOOTH);
this.symbols[2] = symbols[2].getScaledInstance(15, 15, Image.SCALE_SMOOTH);
this.symbols[3] = symbols[3].getScaledInstance(15, 15, Image.SCALE_SMOOTH);
} catch (IOException e) {
e.printStackTrace();
}
updateOrder();
}
public boolean isElementHighlighted(){
return highlightedElementIndex != -1;
}
public boolean isElementMarked(){
return markedElementIndex != -1;
}
public void unmarkElements(){
markedElementIndex = -1;
repaint();
}
public void unlightElements(){
highlightedElementIndex = -1;
repaint();
}
public void addContactListListener(ContactListListener listener){
contactListListener.add(listener);
}
/**
* Updates the order of the displayed elements on base of the {@link Contact}s, their statuses
* and their alphabetical order.
*/
public void updateOrder(){
displayedElements.removeAll(displayedElements);
if(! settings.showGroups & ! settings.showOfflineContacts){
for(ListElement element : elementsInList){
if(element.isContactElement() & ((Contact) element).getStatus() != Status.Offline){
displayedElements.add(element);
}
}
}
if(! settings.showGroups & settings.showOfflineContacts){
for(ListElement element : elementsInList){
if(element.isContactElement()){
displayedElements.add(element);
}
}
}
Collections.sort(displayedElements);
}
public void paint(Graphics g){
BufferedImage bi = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB);
refreshGraphic(bi.getGraphics());
g.drawImage(bi, 0, 0, null);
scrollBarAssessment();
}
private void scrollBarAssessment(){
int listHeight = displayedElements.size() * ELEMENT_HEIGHT;
if(listHeight > getHeight() && listHeight != scrollBarHeight){
scrollBarHeight = listHeight;
for(ContactListListener listener : contactListListener){
listener.scrollBarNeeded(scrollBarHeight);
}
}else if(listHeight <= getHeight() && scrollBarHeight != -1){
scrollBarHeight = -1;
for(ContactListListener listener : contactListListener){
listener.disposeScrollBar();
}
}
}
/**
* Refreshes the graphic representation of the {@link ContactList}.
*/
private void refreshGraphic(Graphics g){
g.setColor(Color.white);
g.fillRect(0, 0, getWidth(), getHeight());
firstElementIndex = virtualAxis / ELEMENT_HEIGHT;
int lastElementIndex = firstElementIndex + (getHeight() / ELEMENT_HEIGHT) + 1;
if(lastElementIndex >= displayedElements.size()){
lastElementIndex = displayedElements.size() - 1;
}
int positionInPanel = (virtualAxis - (firstElementIndex * ELEMENT_HEIGHT));
positionInPanel = 0;
offset = positionInPanel;
positionInPanel *= -1;
Contact contact = null;
Group group = null;
ListElement listElement;
g.setColor(Color.black);
g.setFont(new Font("Tahoma", Font.PLAIN, 11));
for(int i = 0; i <= lastElementIndex - firstElementIndex; i++){
listElement = displayedElements.get(firstElementIndex + i);
if(firstElementIndex + i == highlightedElementIndex){
g.setColor(hightlightColor);
g.fillRect(0, positionInPanel, getWidth(), ELEMENT_HEIGHT);
g.setColor(Color.black);
}
if(firstElementIndex + i == markedElementIndex){
g.setColor(markColor);
g.fillRect(0, positionInPanel, getWidth(), ELEMENT_HEIGHT);
g.setColor(Color.black);
}
if(listElement.isContactElement()){
contact = (Contact) listElement;
switch(contact.getStatus()){
case Online: g.drawImage(symbols[0], 12, positionInPanel + 2, 15, 15, null); break;
case Offline: g.drawImage(symbols[3], 12, positionInPanel + 2, 15, 15, null); break;
case Invisible: g.drawImage(symbols[2], 12, positionInPanel + 2, 15, 15, null); break;
case NotAvailable: g.drawImage(symbols[1], 12, positionInPanel + 2, 15, 15, null); break;
}
}else{
group = (Group) listElement;
}
g.drawString(listElement.getDisplayedName(), 37, positionInPanel + 15);
positionInPanel += 20;
}
}
public int getElementIndexAt(int y){
int element = 0;
if(y <= (ELEMENT_HEIGHT - offset)){
element = firstElementIndex;
}else{
y -= ELEMENT_HEIGHT - offset;
element = y / ELEMENT_HEIGHT + firstElementIndex + 1;
}
return element;
}
@Override
public void mouseClicked(MouseEvent arg0) {
for(ContactListListener listener : contactListListener){
listener.mouseClickedOnList();
}
int element = getElementIndexAt(arg0.getPoint().y);
if(arg0.getButton() == 1 && arg0.getClickCount() == 1){
if(element < displayedElements.size()){
if(markedElementIndex != element){
markedElementIndex = element;
repaint();
}
}else if(markedElementIndex != -1){
markedElementIndex = -1;
repaint();
}
}else if(arg0.getButton() == 1){
if(displayedElements.get(element).isContactElement()){
Contact contact = (Contact) displayedElements.get(element);
markedElementIndex = -1;
for(ContactListListener listener : contactListListener){
listener.chatRequested(contact);
}
}
repaint();
}else if(arg0.getButton() == 3){
if(displayedElements.size() == 0){
for(ContactListListener listener : contactListListener){
listener.contactListSettingRequested(null, arg0.getPoint());
}
}else if(element < displayedElements.size()){
for(ContactListListener listener : contactListListener){
listener.contactListSettingRequested(displayedElements.get(element), arg0.getPoint());
}
}
}
}
@Override
public void mouseEntered(MouseEvent arg0) {
if( ! getCursor().equals(Cursor.DEFAULT_CURSOR)){
setCursor(Cursor.getDefaultCursor());
}
}
public void setVirtualAxis(int axis){
virtualAxis = axis;
highlightedElementIndex = -1;
repaint();
}
@Override
public void mouseExited(MouseEvent arg0) {
if(highlightedElementIndex != -1){
highlightedElementIndex = -1;
repaint();
}
}
@Override
public void mousePressed(MouseEvent arg0) {
// TODO Auto-generated method stub
}
@Override
public void mouseReleased(MouseEvent arg0) {
// TODO Auto-generated method stub
}
@Override
public void mouseDragged(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mouseMoved(MouseEvent e) {
int element = getElementIndexAt(e.getPoint().y);
if(element < displayedElements.size()){
if(highlightedElementIndex != element){
highlightedElementIndex = element;
repaint();
}
}else if(highlightedElementIndex != -1){
highlightedElementIndex = -1;
repaint();
}
}
@Override
public void setBounds(int x, int y, int width, int height) {
super.setBounds(x, y, width, height);
scrollBarAssessment();
}
}