/* * SnarkGui - Main snark program startup class which uses a Gnome UI. * Copyright (C) 2003 Mark J. Wielaard * * This file is part of Snark. * * 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, or (at your option) 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.torrent.snark.gui; /** * Main Snark program startup class that uses a Gnome UI. * * @author Mark Wielaard (mark@klomp.org) */ public class SnarkGui { } /* import org.gnu.glib.Fireable; import org.gnu.glib.Timer; import org.gnu.gnome.App; import org.gnu.gnome.AppBar; import org.gnu.gnome.PreferencesType; import org.gnu.gnome.Program; import org.gnu.gnome.UIInfo; import org.gnu.gtk.AboutDialog; import org.gnu.gtk.Gtk; import org.gnu.gtk.HBox; import org.gnu.gtk.Justification; import org.gnu.gtk.Label; import org.gnu.gtk.ProgressBar; import org.gnu.gtk.SizeGroup; import org.gnu.gtk.SizeGroupMode; import org.gnu.gtk.VBox; import org.gnu.gtk.Widget; import org.gnu.gtk.event.LifeCycleEvent; import org.gnu.gtk.event.LifeCycleListener; import org.gnu.gtk.event.MenuItemEvent; import org.gnu.gtk.event.MenuItemListener; public class SnarkGui implements Runnable, StorageListener, CoordinatorListener, ShutdownListener{ private static final String VERSION = "0.6"; private static final String AUTHOR1 = "Mark J. Wielaard <mark@klomp.org>"; private static final String AUTHOR2 = "Elizabeth Fong <elizabeth@threerings.net>"; private static final String COPYING = "Copyright 2003, Mark J. Wielaard, " + "2006 Three Rings Design" + "\n" + "Distributed under the terms of the " + "GNU General Public License (GPL)"; private static final String COMMENT = "A BitTorrent client, torrent creator and tracker" + " that makes sharing files with your buddies as easy as possible." + "\n\n" + "Snark comes with ABSOLUTELY NO WARRANTY. " + "This is free software, and you are welcome to redistribute it " + "under certain conditions; read the COPYING file for details."; // How often to call the update time which calls fire() per second. static int UPDATE_TIMER = 2; private static SnarkShutdown snarkhook; private String[] args; private String name; private final App app; private final AppBar appbar; private final Widget propertiesItem; private final Widget quitItem; private final Widget aboutItem; private final Label torrentName; private final Label downloadRate; private final Label uploadRate; private final Label piecesCollected; private final SnarkInfoFrame propertiesFrame; // Creates the whole SnarkGnome application. private SnarkGui() { // Main window app = new App("snark", "Snark"); app.addListener(this); // Status - what are we doing, how much have we done - thingy appbar = new AppBar(true, true, PreferencesType.USER); appbar.setStatusText("Snark ready..."); app.setStatusBar(appbar); // Menus UIInfo quitMenuItem = UIInfo.quitItem(this); UIInfo propertiesMenuItem = UIInfo.propertiesItem(this); UIInfo fileMenu[] = { propertiesMenuItem, quitMenuItem, UIInfo.end() }; UIInfo aboutMenuItem = UIInfo.aboutItem(this); UIInfo helpMenu[] = { aboutMenuItem, UIInfo.end() }; UIInfo appMenus[] = { UIInfo.subtree("_File", fileMenu), UIInfo.subtree("_Help", helpMenu), UIInfo.end() }; // Add them to the window and make them give status info. app.createMenus(appMenus); app.installMenuHints(appMenus); // Add some properties to the window // TorrentInfo that we are sharing HBox torrentBox = new HBox(false, 6); Label torrent = new Label("Sharing:"); torrent.setJustification(Justification.RIGHT); torrent.setAlignment(0d, 0.5d); torrentName = new Label("<unknown>"); torrentName.setJustification(Justification.LEFT); torrentName.setAlignment(0d, 0.5d); torrentBox.packStart(torrent); torrentBox.packEnd(torrentName); // Download rate HBox downloadBox = new HBox(false, 6); Label download = new Label("Download:"); download.setJustification(Justification.RIGHT); download.setAlignment(0d, 0.5d); downloadRate = new Label("0 KB/s"); downloadRate.setJustification(Justification.LEFT); downloadRate.setAlignment(0d, 0.5d); downloadBox.packStart(download); downloadBox.packEnd(downloadRate); // Upload rate HBox uploadBox = new HBox(false, 6); Label upload = new Label("Upload:"); upload.setJustification(Justification.RIGHT); upload.setAlignment(0d, 0.5d); uploadRate = new Label("0 KB/s"); uploadRate.setJustification(Justification.LEFT); uploadRate.setAlignment(0d, 0.5d); uploadBox.packStart(upload); uploadBox.packEnd(uploadRate); // Pieces HBox piecesBox = new HBox(false, 6); Label pieces = new Label("Pieces:"); pieces.setJustification(Justification.RIGHT); pieces.setAlignment(0d, 0.5d); piecesCollected = new Label(""); piecesCollected.setJustification(Justification.LEFT); piecesCollected.setAlignment(0d, 0.5d); piecesBox.packStart(pieces); piecesBox.packEnd(piecesCollected); // Group labels to get the same sizes. SizeGroup labelGroup = new SizeGroup(SizeGroupMode.HORIZONTAL); labelGroup.addWidget(torrent); labelGroup.addWidget(download); labelGroup.addWidget(upload); labelGroup.addWidget(pieces); // Group values to get the same sizes. SizeGroup valueGroup = new SizeGroup(SizeGroupMode.HORIZONTAL); valueGroup.addWidget(torrentName); valueGroup.addWidget(downloadRate); valueGroup.addWidget(uploadRate); valueGroup.addWidget(piecesCollected); // Put it all together VBox infoBox = new VBox(true, 6); infoBox.setBorderWidth(12); infoBox.packStart(torrentBox); infoBox.packStart(downloadBox); infoBox.packStart(uploadBox); infoBox.packStart(piecesBox); app.setContent(infoBox); // HACK - Somehow the application window is to small. // Make sure it has room for at least the progress bar and status. // Sadly we cannot get the default size of the progress bar... // So we just guess some width. BAD! app.setDefaultSize(300, -1); // We are ready to show and tell! app.showAll(); // Create, but don't show properties window. propertiesFrame = new SnarkInfoFrame(_snark); // HACK - Only after we show everything can we get the widgets. propertiesItem = propertiesMenuItem.getWidget(); quitItem = quitMenuItem.getWidget(); aboutItem = aboutMenuItem.getWidget(); // Check for progress and update the progress bar every half second. Timer timer = new Timer(1000 / UPDATE_TIMER, this); timer.start(); } // See lifeCycleEvent() and fire(). private boolean shutdown_now = false; // Handles Menu events (quit, about, ...). public void menuItemEvent (MenuItemEvent event) { Object source = event.getSource(); if (source.equals(propertiesItem)) { propertiesFrame.show(); } else if (source.equals(quitItem)) { quit(); } else if (source.equals(aboutItem)) { about(); } else { log.log(Level.WARNING, "Unknown event: " + event + " from source: " + source); } } // Called when the application should quit. private void quit () { activity = SHUTDOWN; if (snarkhook != null) { Runtime.getRuntime().removeShutdownHook(snarkhook); snarkhook.start(); } else { shutdown(); } } // Called by the shutdown hook public void shutdown () { Gtk.mainQuit(); System.exit(0); } // Called when we have to show info about the program. private void about () { String[] authors = { AUTHOR1, AUTHOR2 }; AboutDialog about = new AboutDialog(); about.setName("The Hunting of the Snark Project"); about.setVersion(VERSION); about.setCopyright(COPYING); about.setComments(COMMENT); about.setAuthors(authors); about.show(); } // Creates the actual Snark object. Runs in the background public void run () { _snark = SnarkApplication.parseArguments(args, this, this); snarkhook = new SnarkShutdown(_snark.storage, _snark.coordinator, _snark.acceptor, _snark.trackerclient, this); Runtime.getRuntime().addShutdownHook(snarkhook); } // Starts snark with a Gnome UI. public static void main (String[] args) { // Initialize Gnome libraries and handle common arguments. Program.initGnomeUI("Snark", VERSION, args); // Setup the main application window SnarkGui snarkgnome = new SnarkGui(); snarkgnome.args = args; // Initialize the Snark in a separate thread Thread thread = new Thread(snarkgnome); thread.start(); // Go! Handle events... Gtk.main(); } // True when progress() is called, cleared when madeProgress() is called. // Change/Check this flag only while holding the lock on this. private boolean progress = false; private synchronized void progress () { progress = true; } public void peerChange (PeerCoordinator coordinator, Peer peer) { activity = getActivity(); progress(); } public void storageCreateFile (Storage storage, String name, long length) { // We should display something about this... activity = ALLOCATING; } // How much storage space has been allocated private long allocated = 0; public void storageAllocated (Storage storage, long length) { activity = ALLOCATING; allocated += length; if (allocated == _snark.meta.getTotalLength()) { ; // We are done, but we done't care } progress(); } // How many pieces have been checked. private int checked = 0; boolean prechecking = true; public void storageChecked (Storage storage, int num, boolean checked) { if (prechecking) { activity = CHECKING; } else { // Should we display something about BAD pieces? activity = getActivity(); } this.checked++; progress(); } public void storageAllChecked (Storage storage) { prechecking = false; activity = getActivity(); } // Returns the current activity by checking the storage and peer // coordinator. private String getActivity () { String activity; // No turning back from a shutdown if (this.activity == SHUTDOWN) { return SHUTDOWN; } if (_snark.coordinator != null && _snark.coordinator.peers != null) { synchronized (_snark.coordinator.peers) { if (_snark.coordinator.peers.size() > 0 && (_snark.coordinator.getDownloaded() > 0 || _snark.coordinator.getUploaded() > 0)) { if (_snark.storage != null && _snark.storage.complete()) { activity = SHARING; } else { activity = COLLECTING; } } else { activity = CONNECTING; } } } else { activity = CONNECTING; } return activity; } // Called by fire() to check if there was any progress recently. Returns // true when progress() was called since the last call to madeProgress(). // Synchronized to make sure we don't miss any progress events. private synchronized boolean madeProgress () { boolean result = progress; progress = false; return result; } // Used for keeping track of the up and download rate // Updated every second in fire(). private long lastDownloaded = 0; private long downb = 0; private long lastUploaded = 0; private long upb = 0; private static final String STARTUP = "Starting up"; private static final String ALLOCATING = "Creating files"; private static final String CHECKING = "Checking files"; private static final String CONNECTING = "Connecting to peers"; private static final String COLLECTING = "Collecting pieces"; private static final String SHARING = "Sharing pieces"; private static final String SHUTDOWN = "Shutting down"; private String activity = STARTUP; private String lastActivity = null; // Sets upload and download rates texts. Used for everything gtk+/gnome // since that seems the most thread save way. public boolean fire () { // XXX - Little bit of a hack, when the close box has been pressed // anything to do with the main window seems to crash... if (shutdown_now) { return false; } // What are we doing? if (activity != lastActivity) { // Update status text and reset progress bar appbar.setStatusDefault(activity); ProgressBar progressBar = appbar.getProgressBar(); progressBar.setFraction(0); progressBar.setText(""); lastActivity = activity; progress(); } // Do we know the torrent name we are sharing now? if (_snark.meta != null && name == null) { name = _snark.meta.getName(); torrentName.setText(name); } // Calculate and update download and upload speeds if (_snark.coordinator != null && (activity == COLLECTING || activity == SHARING)) { // Calculate and show download rate. long downloaded = _snark.coordinator.getDownloaded(); long diff = downloaded - lastDownloaded; downb -= downb / 10; downb += diff / 10; long kb = downb / (1024 / UPDATE_TIMER); String totalDown; if (downloaded >= (10 * 1024 * 1024)) { totalDown = (downloaded / (1024 * 1024)) + " MB"; } else { totalDown = (downloaded / 1024) + " KB"; } downloadRate.setText(kb + " KB/s (" + totalDown + ")"); lastDownloaded = downloaded; // Calculate and show upload rate. long uploaded = _snark.coordinator.getUploaded(); diff = uploaded - lastUploaded; upb -= upb / 10; upb += diff / 10; kb = upb / (1024 / UPDATE_TIMER); String totalUp; if (uploaded >= (10 * 1024 * 1024)) { totalUp = (uploaded / (1024 * 1024)) + " MB"; } else { totalUp = (uploaded / 1024) + " KB"; } uploadRate.setText(kb + " KB/s (" + totalUp + ")"); lastUploaded = uploaded; } // Did we make progress? boolean update = madeProgress(); if (_snark.meta != null && _snark.storage != null && update) { // Pieces and percentagees int pieces = _snark.meta.getPieces(); int needed = _snark.storage.needed(); int got = pieces - needed; long percentage; if (activity == ALLOCATING) { percentage = (100 * allocated) / _snark.meta.getTotalLength(); } else if (activity == CHECKING) { percentage = (100 * checked) / pieces; } else { percentage = (100 * got) / pieces; } // Update progress bar double progress = percentage / 100d; ProgressBar progressBar = appbar.getProgressBar(); if (activity == ALLOCATING || activity == CHECKING || activity == COLLECTING) { progressBar.setFraction(progress); progressBar.setText(percentage + "%"); } else { progressBar.pulse(); } // Update collected pieces count piecesCollected.setText(got + " of " + pieces); if (_snark.coordinator != null) { propertiesFrame.update(_snark.coordinator.getPeers()); } } // We want to run again and again and again... return true; } // documentation inherited from interface LifeCycleListener public void lifeCycleEvent (LifeCycleEvent event) { } // documentation inherited from interface LifeCycleListener public boolean lifeCycleQuery (LifeCycleEvent arg0) { shutdown_now = true; quit(); return false; } protected Snark _snark; // The Java logger used to process our log events. protected static final Logger log = Logger.getLogger("org.torrent.snark.gui"); } */