package cz.cuni.mff.inetpaint.communications;
import cz.cuni.mff.inetpaint.communications.swing.ClosableTabComponent;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import java.util.*;
import cz.cuni.mff.inetpaint.*;
import cz.cuni.mff.inetpaint.dialog.*;
import org.jivesoftware.smack.packet.*;
/**
* Třída reprezentující hlavní okno komunikací.
* @author Jindřich Helcl
*/
public class CommunicationFrame extends JFrame {
private static final Dimension MIN_SIZE = new Dimension(400,360);
private static CommunicationFrame instance;
/** Static methods */
private static CommunicationFrame getInstance() {
if(instance == null) {
instance = new CommunicationFrame();
// až se okno zavře, tak se musí zrušit objekt - proč?
/*
EventQueue.invokeLater(new Runnable() {
public void run() {
instance.addWindowListener(new WindowAdapter() {
@Override
public void windowClosed(WindowEvent e) {
super.windowClosed(e);
instance = null;
}
});
}
});
*
*/
}
return instance;
}
/**
* Přepne okno na danou komunikaci. Pokud není otevřené, zobrazí se.
* @param communication Komunikace, která se má zobrazit
*/
public static void showCommunication(AbstractCommunication communication) {
showCommunication(communication, false);
}
/**
* Přepne okno na danou komunikaci. Pokud není otevřené, zobrazí se.
* @param communication Komunikace, která se má zobrazit
* @param automatic Pokud je tento flag nastaven na {@literal true}, okno se otevře na pozadí
*/
public static void showCommunication(AbstractCommunication communication, boolean automatic) {
CommunicationFrame validInstance = getInstance();
// pokud je okno zavřené a pokud jde o automatické otevření, otevřu ho minimalizovaně..
if(automatic && !validInstance.isVisible()) {
validInstance.setExtendedState(ICONIFIED);
}
// pokud to je manuál, vždy nastavím extended state na ten uloženej v preferences a otevřu ho
else if (!automatic){
validInstance.setExtendedState(NORMAL);
}
validInstance.setVisible(true);
// pokud komunikace není zobrazena, zobrazím jí.
int index = validInstance.addCommunication(communication);
// přepnu panel na danou komunikaci (jen když neni automatic)
if(!automatic) {
validInstance.selectTabAt(index);
}
}
/**
* Skryje danou komunikaci z okna.
* @param communication Komunikace, která se má skrýt
*/
public static void hideCommunication(AbstractCommunication communication) {
CommunicationFrame validInstance = getInstance();
validInstance.removeCommunication(communication);
}
/**
* Přidá objekt třídy {@link CommunicationListener}, který zpracovává události oznamování
* příchozích paketů.
* @param listener Listener, který se má přidat
*/
public static void addCommunicationListener(CommunicationListener listener) {
getInstance().showListeners.add(listener);
}
/**
* Odebere objekt třídy {@link CommunicationListener}, který zpracovával události oznamování
* příchozích paketů.
* @param listener Listener, který se má odebrat
*/
public static void removeCommunicationListener(CommunicationListener listener) {
getInstance().showListeners.remove(listener);
}
/**
* Zobrazí vizitku daného uživatele.
* @param JID Jabber ID uživatele
*/
public static void showVCardDialog(String JID) {
if(instance == null) return;
VCardDialog vc = new VCardDialog(instance, JID);
vc.setLocationRelativeTo(instance);
vc.setVisible(true);
}
/**
* Pošle oznámení všem komunikacím o příchozím paketu.
* @param packet Příchozí paket
* @see CommunicationListener#communicationNotified(java.util.Collection)
*/
public static void notifyPacket(Packet packet) {
getInstance().fireCommunicationNotifiedEvent(packet);
}
/**
* Zavře a zničí aktuální validní instanci tohoto okna (používá se při odhlášení)
*/
public static void destroy() {
CommunicationFrame validInstance = getInstance();
validInstance.processWindowEvent(new WindowEvent(validInstance, WindowEvent.WINDOW_CLOSING));
instance = null;
}
/** End of static methods */
/** Private fields and methods */
private Collection<CommunicationListener> showListeners;
private ArrayList<AbstractCommunication> showedCommunications;
private JTabbedPane commTabs;
/*
public void communicationShowed(AbstractCommunication communication) {
// všechny panely, které zobrazují nepřečtenou zprávu, která se teď zobrazila, změní ikonku.
// TODO panely, které zorazují ještě jinou nepřečtenou zprávu, by změnit ikonku neměly.
// TODO prozatimní řešení: vsechny panely daneho JID zmeni ikonku na normal
// návrh: panely budou mít flag "nepřečtená komunikace", a budou se ovládat metodou
// "komunikace přečtena" s argumentem toho, co bylo přečtený. sami pak změní svojí ikonku podle toho.
// dobře se to hrotí pomocí ID paketů. komunikace bude mít přístup k seznamu nepřečtenejch ídéček.
// tohle teda nakonec vůbec nebude implementovat communicationlistener
// nepřečtený ID budou uložený kde?
for(int i = 0; i < commTabs.getTabCount(); ++i) {
AbstractCommunicationPanel panel = (AbstractCommunicationPanel)commTabs.getComponentAt(i);
if((panel instanceof ChatPanel) || (panel instanceof PaintPanel)) {
if(panel.getJID().equalsIgnoreCase(JID)) {
ChatTabComponent tab = (ChatTabComponent) panel.getTabComponent();
tab.setMessageNotifyIcon(false);
}
}
}
}
*/
private boolean isWindowVisible() {
boolean visible = getExtendedState() != ICONIFIED &&
isFocused();
return visible;
}
private boolean isMessageVisible(Message message) {
// vrátí true, když je vidět komunikace, ve která chytí tuto zprávu.
AbstractCommunication comm = getSelectedCommunication();
boolean visible = isWindowVisible() &&
comm != null &&
comm.getAcceptedMessagesFilter().accept(message);
return visible;
}
private AbstractCommunication getSelectedCommunication() {
int index = commTabs.getSelectedIndex();
if(index == -1) return null;
return showedCommunications.get(index);
}
private void selectTabAt(int index) {
commTabs.setSelectedIndex(index);
}
private void removeCommunication(AbstractCommunication communication) {
commTabs.remove(communication.getComponent());
showedCommunications.remove(communication);
if(commTabs.getTabCount() == 0) {
dispose();
}
}
private int addCommunication(final AbstractCommunication communication) {
if(!showedCommunications.contains(communication)) {
showedCommunications.add(communication);
final Component component = communication.getComponent();
final ClosableTabComponent tab = communication.getTabComponent();
commTabs.addTab(communication.getJID(), component);
int index = commTabs.indexOfComponent(component);
if(index != showedCommunications.indexOf(communication)) {
throw new AssertionError("Tab-Comm Index consistency corrupted.");
}
commTabs.setTabComponentAt(index, tab);
tab.addCloseActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
hideCommunication(communication);
tab.removeCloseActionListener(this);
}
});
}
return showedCommunications.indexOf(communication);
}
private void fireCommunicationShowedEvent() {
if(!isWindowVisible()) return;
AbstractCommunication communication = getSelectedCommunication();
if(communication == null) return;
final CommunicationManager manager = CommunicationManager.getInstance();
manager.communicationShowed(communication);
for(CommunicationListener list : showListeners) {
list.communicationShowed(manager.getNotifiedCommunications());
}
}
private void fireCommunicationNotifiedEvent(Packet packet) {
if(!(packet instanceof Message)) return;
// je ta zpráva vidět? tzn. okno má focus, neni ICONIFIED a selectedIndex ukazuje na komunikaci, který to přijde
Message message = (Message) packet;
// pokud zpráva neni vidět, vystřelim
if(!isMessageVisible(message)) {
final CommunicationManager manager = CommunicationManager.getInstance();
manager.packetNotified(packet);
for(CommunicationListener list : showListeners) {
list.communicationNotified(manager.getNotifiedCommunications());
}
}
}
private void initComponents() {
ResourceBundle rb = Program.getStringsBundle();
setMinimumSize(MIN_SIZE);
int extendedState = UserPreferences.getChatFrameState();
if(extendedState == NORMAL)
setBounds(UserPreferences.getChatFrameLocation());
else
setExtendedState(UserPreferences.getChatFrameState());
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
UserPreferences.setChatFrameLocation(CommunicationFrame.this.getBounds());
UserPreferences.setChatFrameState(CommunicationFrame.this.getExtendedState());
// zavřít všechny komunikace - ani to neni potřeba přece!
//showedCommunications.clear();
}
});
setTitle(rb.getString("chatTitle"));
ArrayList<Image> icons = new ArrayList<Image>(2);
icons.add(Program.getImage("icons/icon16.png"));
icons.add(Program.getImage("icons/icon32.png"));
setIconImages(icons);
commTabs = new JTabbedPane();
commTabs.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
fireCommunicationShowedEvent();
}
});
addWindowFocusListener(new WindowAdapter() {
@Override
public void windowGainedFocus(WindowEvent e) {
fireCommunicationShowedEvent();
}
});
add(commTabs);
}
private CommunicationFrame() {
this.showedCommunications = new ArrayList<AbstractCommunication>(15);
this.showListeners = new ArrayList<CommunicationListener>(15);
initComponents();
}
}