package com.limegroup.gnutella.gui;
import java.io.File;
import java.util.Set;
import java.util.Vector;
import javax.swing.SwingUtilities;
import com.limegroup.gnutella.ActivityCallback;
import com.limegroup.gnutella.Connection;
import com.limegroup.gnutella.Downloader;
import com.limegroup.gnutella.FileManagerEvent;
import com.limegroup.gnutella.GUID;
import com.limegroup.gnutella.MediaType;
import com.limegroup.gnutella.RemoteFileDesc;
import com.limegroup.gnutella.RouterService;
import com.limegroup.gnutella.Uploader;
import com.limegroup.gnutella.browser.MagnetOptions;
import com.limegroup.gnutella.chat.Chatter;
import com.limegroup.gnutella.gui.chat.ChatUIManager;
import com.limegroup.gnutella.gui.download.DownloaderUtils;
import com.limegroup.gnutella.gui.search.SearchInformation;
import com.limegroup.gnutella.gui.search.SearchMediator;
import com.limegroup.gnutella.search.HostData;
import com.limegroup.gnutella.settings.DaapSettings;
import com.limegroup.gnutella.settings.QuestionsHandler;
import com.limegroup.gnutella.settings.iTunesSettings;
import com.limegroup.gnutella.util.CommonUtils;
import com.limegroup.gnutella.util.StringUtils;
import com.limegroup.gnutella.util.Switch;
import com.limegroup.gnutella.version.UpdateInformation;
/**
* This class is the gateway from the backend to the frontend. It
* delegates all callbacks to the appropriate frontend classes, and it
* also handles putting calls onto the Swing thread as necessary.
*
* It implements the <tt>ActivityCallback</tt> callback interface, designed
* to make it easy to swap UIs.
*/
public final class VisualConnectionCallback implements ActivityCallback {
/**
* Constructs a new VisualConnectionCallback.
*/
VisualConnectionCallback() {}
///////////////////////////////////////////////////////////////////////////
// Connection-related callbacks
///////////////////////////////////////////////////////////////////////////
/**
* Handle a new connection.
*/
public void connectionInitializing(Connection c) {
Runnable doWorkRunnable = new ConnectionInitializing(c);
SwingUtilities.invokeLater(doWorkRunnable);
}
/**
* Change the status of a connection when it's been fully initialized
*/
public void connectionInitialized(Connection c) {
Runnable doWorkRunnable = new ConnectionInitialized(c);
SwingUtilities.invokeLater(doWorkRunnable);
}
/**
* Handle a removed connection.
*/
public void connectionClosed(Connection c) {
Runnable doWorkRunnable = new ConnectionClosed(c);
SwingUtilities.invokeLater(doWorkRunnable);
}
/**
* Handle a new connection.
*/
private class ConnectionInitializing implements Runnable {
private Connection c;
public ConnectionInitializing(Connection c) {
this.c = c;
}
public void run() {
mf().getConnectionMediator().add(c);
}
}
/**
* Change the status of a connection when it's been fully initialized
*/
private class ConnectionInitialized implements Runnable {
private Connection c;
public ConnectionInitialized(Connection c) {
this.c = c;
}
public void run() {
mf().getConnectionMediator().update(c);
}
}
/**
* Handle a removed connection.
*/
private class ConnectionClosed implements Runnable {
private Connection c;
public ConnectionClosed(Connection c) {
this.c = c;
}
public void run() {
mf().getConnectionMediator().remove(c);
}
}
///////////////////////////////////////////////////////////////////////////
// Query-related callbacks
///////////////////////////////////////////////////////////////////////////
/**
* Handle to the class that handles query strings.
*/
private final HandleQueryString HANDLE_QUERY_STRING = new HandleQueryString();
/**
* Add a query string to the monitor screen
*/
public void handleQueryString(String query) {
HANDLE_QUERY_STRING.addQueryString(query);
}
/**
* Add a query reply to a query screen
*/
public void handleQueryResult(final RemoteFileDesc rfd,
final HostData data,
final Set locs) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
SearchMediator.handleQueryResult(rfd, data, locs);
}
});
}
/**
* @return true if the guid is still viewable to the user, else false.
*/
public boolean isQueryAlive(GUID guid) {
return SearchMediator.queryIsAlive(guid);
}
/**
* Add a query string to the monitor screen
*/
private class HandleQueryString implements Runnable {
private Vector list;
private boolean active;
public HandleQueryString() {
list = new Vector();
active = false;
}
public void addQueryString(String query) {
list.add(query);
if (active == false) {
active = true;
SwingUtilities.invokeLater(this);
}
}
public void run() {
String query;
while (list.size() > 0) {
query = (String) list.elementAt(0);
list.remove(0);
mf().getMonitorView().handleQueryString(query);
}
active = false;
}
}
///////////////////////////////////////////////////////////////////////////
// Files-related callbacks
///////////////////////////////////////////////////////////////////////////
/**
* File manager finished loading.
*/
public void fileManagerLoaded() {
if (DaapSettings.DAAP_ENABLED.getValue()) {
Runnable r = new Runnable() {
public void run() {
DaapManager.instance().fileManagerLoaded();
}
};
GUIMediator.instance().schedule(r);
}
}
/**
* This method notifies the frontend that the data for the
* specified shared <tt>File</tt> instance has been
* updated.
*
* @param file the <tt>File</tt> instance for the shared file whose
* data has been updated
*/
public void handleSharedFileUpdate(final File file) {
/**
* NOTE: Pass this off directly to the library
* so it can discard the update if the directory
* of the file isn't selected.
* This reduces the amount of Runnables created
* by a very large amount.
*/
mf().getLibraryMediator().updateSharedFile(file);
}
/**
* Handles events created by the FileManager. Passes these events on to DAAP
* or the Library.
*/
public void handleFileEvent(final FileManagerEvent evt) {
if (DaapSettings.DAAP_ENABLED.getValue()
&& DaapManager.instance().isEnabled()) {
Runnable r = new Runnable() {
public void run() {
DaapManager.instance().handleFileManagerEvent(evt);
}
};
GUIMediator.instance().schedule(r);
}
SwingUtilities.invokeLater(new Runnable() {
public void run() {
mf().getLibraryMediator().handleFileManagerEvent(evt);
}
});
}
public void fileManagerLoading() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
mf().getLibraryMediator().clearLibrary();
}
});
if (DaapSettings.DAAP_ENABLED.getValue()) {
Runnable r = new Runnable() {
public void run() {
DaapManager.instance().fileManagerLoading();
}
};
GUIMediator.instance().schedule(r);
}
}
///////////////////////////////////////////////////////////////////////////
// Download-related callbacks
///////////////////////////////////////////////////////////////////////////
public void addDownload(Downloader mgr) {
Runnable doWorkRunnable = new AddDownload(mgr);
SwingUtilities.invokeLater(doWorkRunnable);
}
public void removeDownload(Downloader mgr) {
Runnable doWorkRunnable = new RemoveDownload(mgr);
SwingUtilities.invokeLater(doWorkRunnable);
if (iTunesSettings.ITUNES_SUPPORT_ENABLED.getValue()
&& mgr.getState() == Downloader.COMPLETE) {
iTunesMediator.instance().addSong(mgr.getSaveFile());
}
}
public void downloadsComplete() {
Finalizer.setDownloadsComplete();
}
/**
* Show active downloads
*/
public void showDownloads() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
GUIMediator.instance().setWindow(GUIMediator.SEARCH_INDEX);
}
});
}
private class AddDownload implements Runnable {
private Downloader mgr;
public AddDownload(Downloader mgr) {
this.mgr = mgr;
}
public void run() {
mf().getDownloadMediator().add(mgr);
}
}
private class RemoveDownload implements Runnable {
private Downloader mgr;
public RemoveDownload(Downloader mgr) {
this.mgr = mgr;
}
public void run() {
mf().getDownloadMediator().remove(mgr);
mf().getLibraryMediator().quickRefresh();
SearchMediator.updateResults();
}
}
///////////////////////////////////////////////////////////////////////////
// Upload-related callbacks
///////////////////////////////////////////////////////////////////////////
public void addUpload(Uploader mgr) {
Runnable doWorkRunnable = new AddUpload(mgr);
SwingUtilities.invokeLater(doWorkRunnable);
}
public void removeUpload(Uploader mgr) {
Runnable doWorkRunnable = new RemoveUpload(mgr);
SwingUtilities.invokeLater(doWorkRunnable);
}
public void uploadsComplete() {
Finalizer.setUploadsComplete();
}
private class AddUpload implements Runnable {
private Uploader up;
public AddUpload(Uploader up) {
this.up = up;
}
public void run() {
mf().getUploadMediator().add(up);
}
}
private class RemoveUpload implements Runnable {
private Uploader mgr;
public RemoveUpload(Uploader mgr) {
this.mgr = mgr;
}
public void run() {
mf().getUploadMediator().remove(mgr);
}
}
///////////////////////////////////////////////////////////////////////////
// Chat-related callbacks
///////////////////////////////////////////////////////////////////////////
/**
* Adds a new chat session, encapsulated in the specified
* <tt>Chatter</tt> instance.
*
* @param chatter the <tt>Chatter</tt> instance that provides all
* data access regarding the chat session
*/
public void acceptChat(final Chatter chatter) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
ChatUIManager.instance().acceptChat(chatter);
}
});
}
/**
* Receives a new chat message for a specific <tt>Chatter</tt>
* instance.
*
* @param chatter the <tt>Chatter</tt> instance that is receiving
* a new message
*/
public void receiveMessage(final Chatter chatter) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
ChatUIManager.instance().receiveMessage(chatter);
}
});
}
/**
* Specifies that the given chat host is no longer available, thereby
* ending the chat session.
*
* @param chatter the <tt>Chatter</tt> instance for the chat session
* that is terminating
*/
public void chatUnavailable(final Chatter chatter) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
ChatUIManager.instance().chatUnavailable(chatter);
}
});
}
/**
* Display an error message for the specified chat session.
*
* @param chatter the <tt>Chatter</tt> instance to show an error for
* @param str the error to display
*/
public void chatErrorMessage(final Chatter chatter, final String str) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
ChatUIManager.instance().chatErrorMessage(chatter, str);
}
});
}
///////////////////////////////////////////////////////////////////////////
// Other stuff
///////////////////////////////////////////////////////////////////////////
/**
* Notification that the address has changed.
*/
public void addressStateChanged() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// don't touch GUI code if it isn't constructed.
// this is necessary here only because addressStateChanged
// is triggered by Acceptor, which is init'd prior to the
// GUI actually existing.
if (GUIMediator.isConstructed())
SearchMediator.addressChanged();
}
});
}
public void disconnected() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
if (GUIMediator.isConstructed())
GUIMediator.disconnected();
}
});
}
/**
* Pops up a dialog that the user is attempting to share a sensitive
* directory, and allows the user to either share or not share
* the folder. Returns true if the sensitive directory should be shared.
*/
public boolean warnAboutSharingSensitiveDirectory(final File dir) {
final Switch share = new Switch();
GUIMediator.safeInvokeAndWait(new Runnable() {
public void run() {
// Use unicode char for non-breaking space so that
// directory name is listed all on one line.
String dirName = GUIUtils.convertToNonBreakingSpaces(4, dir.getAbsolutePath());
int retval = GUIMediator.showYesNoMessage(
"MESSAGE_SENSITIVE_SHARE_TOP",
"\n\n" + dirName + "\n\n",
"MESSAGE_SENSITIVE_SHARE_BOTTOM");
if (retval == MessageService.YES_OPTION) {
share.turnOn();
RouterService.getFileManager().validateSensitiveFile(dir);
} else {
share.turnOff();
RouterService.getFileManager().invalidateSensitiveFile(dir);
}
}
});
return share.isOn();
}
public void setAnnotateEnabled(final boolean enabled) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
mf().getLibraryMediator().setAnnotateEnabled(enabled);
}
});
}
/**
* Notification that a new update is available.
*/
public void updateAvailable(UpdateInformation update) {
GUIMediator.instance().showUpdateNotification(update);
}
/**
* Display an error message for a ResultPanel (if it still exists)
* @param guid The GUID of the ResultPanel.
*/
public void browseHostFailed(final GUID guid) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
SearchMediator.browseHostFailed(guid);
}
});
}
/**
* Shows the user a message informing her that a file being downloaded
* is corrupt.
* <p>
* This method MUST call dloader.discardCorruptDownload(boolean b)
* otherwise there will be threads piling up waiting for a notification
*/
public void promptAboutCorruptDownload(Downloader downloader) {
final Downloader dloader = downloader;
SwingUtilities.invokeLater(new Runnable() {
public void run() {
int resp=GUIMediator.showYesNoMessage(
"MESSAGE_FILE_CORRUPT",
dloader.getSaveFile().getName(),
"MESSAGE_CONTINUE_DOWNLOAD",
QuestionsHandler.CORRUPT_DOWNLOAD);
// discard if they didn't want to save.
dloader.discardCorruptDownload(
resp == MessageService.NO_OPTION);
}
});
}
/**
* Tell the GUI to deiconify.
*/
public void restoreApplication() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
GUIMediator.restoreView();
}
});
}
/**
* Notification of a component loading.
*/
public void componentLoading(final String component) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
GUIMediator.setSplashScreenString(
GUIMediator.getStringResource("SPLASH_STATUS_COMPONENT_LOADING_" +
component));
}
});
}
/**
* Indicates that the firewalled state of this has changed.
*/
public void acceptedIncomingChanged(final boolean status) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
GUIMediator.instance().getStatusLine().updateFirewallLabel(status);
}
});
}
public String getHostValue(String key) {
return GUIMediator.getStringResource(key);
}
/**
* Returns the MainFrame.
*/
private MainFrame mf() {
return GUIMediator.instance().getMainFrame();
}
/**
* Returns true since we want to kick off the magnet downloads ourselves.
*/
public boolean handleMagnets(final MagnetOptions[] magnets) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
boolean oneSearchStarted = false;
for (int i = 0; i < magnets.length; i++) {
// spawn search for keyword only magnet
if (magnets[i].isKeywordTopicOnly() && !oneSearchStarted) {
String query = StringUtils.createQueryString
(magnets[i].getKeywordTopic());
SearchInformation info =
SearchInformation.createKeywordSearch
(query, null, MediaType.getAnyTypeMediaType());
if (SearchMediator.validateInfo(info)
== SearchMediator.QUERY_VALID) {
oneSearchStarted = true;
SearchMediator.triggerSearch(info);
}
}
else {
DownloaderUtils.createDownloader(magnets[i]);
}
}
if (magnets.length > 0) {
GUIMediator.instance().setWindow(GUIMediator.SEARCH_INDEX);
}
}
});
return true;
}
}