/* Jungle Monkey
* Copyright (C) 1999-2001 The Regents of the University of Michigan
*
* 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
* (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
*/
#include "ggui_download.h"
#include "ggui.h"
#include "ggui_debug.h"
#include "ggui_files.h"
#include "ggui_properties.h"
#include "ggui_search.h"
#include "pixmaps.h"
#include "util/util.h"
#include "util/jmgnet.h"
#include "mtp/mtp_client.h"
#include <sys/types.h>
#include <errno.h>
#include <string.h>
/* ******************** */
typedef struct _Download
{
GURL* url;
gchar* path;
GSHA* sha;
MtpClient* mtp_client;
gboolean have_file;
} Download;
typedef enum
{
IT_CANCEL_DOWNLOAD,
IT_SEPARATOR1,
IT_PROPERTIES,
} Item;
static GladeXML* menu_xml;
static GtkCList* download_clist;
static GtkWidget* download_popup;
static Download* selected_dl;
static GHashTable* url_to_dl;
static void shutdown_hfunc (gpointer key, gpointer value, gpointer user_data);
static Download* download_new (const GURL* url, const gchar* p, const GSHA* sha);
#define download_get(U) ((Download*) g_hash_table_lookup(url_to_dl, (U)))
static gchar* get_path (const gchar* p, const gchar* name);
static void file_func (MtpClient* client, MtpClientStatus status, gpointer user_data);
static void download_update (Download* dl);
static void download_delete (Download* dl);
void on_download_select_row(GtkCList* list, gint row, gint column, GdkEvent* event);
void on_download_unselect_row(GtkCList* list, gint row, gint column, GdkEvent* event);
gint on_download_button_press (GtkCList* list, GdkEventButton *event, gpointer user_data);
static void toolbar_append (Item item, GdkPixmap* gpixmap, GdkBitmap* gbitmap,
const gchar* tooltip, void* func);
static void update_toolbar (void);
static void update_items (void* widget);
static void show_item (void* widget, Item it);
#define POPUPFUNC(F) void F(gpointer callback_data, guint callback_action, GtkWidget* widget)
POPUPFUNC(on_download_cancel_download);
POPUPFUNC(on_download_properties);
void
ggui_download_init (void)
{
menu_xml = ggui_get_glade_xml("download_popup", "master_popup.glade");
g_assert (menu_xml != NULL);
glade_xml_signal_autoconnect(menu_xml);
download_clist = GTK_CLIST(glade_xml_get_widget(jmw_xml, "jmdownloadclist"));
download_popup = GTK_WIDGET(glade_xml_get_widget(menu_xml, "download_popup"));
g_assert (download_clist != NULL);
g_assert (download_popup != NULL);
gtk_clist_column_title_passive (download_clist, 0);
gtk_clist_column_title_passive (download_clist, 1);
ggui_set_tooltip (GTK_WIDGET(download_clist), _("Files you are downloading."));
url_to_dl = g_hash_table_new (gnet_url_hash, gnet_url_equal);
selected_dl = NULL;
/* TODO: Restore incomplete downloads */
}
void
ggui_download_shutdown (void)
{
/* TODO: Serialize incomplete downloads */
g_hash_table_foreach (url_to_dl, shutdown_hfunc, NULL);
g_hash_table_destroy (url_to_dl);
url_to_dl = NULL;
selected_dl = NULL;
}
static void
shutdown_hfunc (gpointer key, gpointer value, gpointer user_data)
{
Download* dl = (Download*) value;
download_delete (dl);
}
/* ******************** */
void
ggui_download_get (const GURL* url, const gchar* path, const GSHA* sha)
{
Download* dl;
g_return_if_fail (url);
/* Ignore non-MTP */
if (SAFESTRCMP (url->protocol, "mtp"))
return;
/* Download if we don't have it */
dl = download_get (url);
if (!dl)
{
dl = download_new (url, path, sha);
}
/* Redownload if we don't have it (there was an error before) */
else if (!dl->mtp_client && !dl->have_file)
{
/* Redownload the file */
if (dl->path)
g_free (dl->path);
dl->path = get_path (path, gnet_url_basename(url));
g_return_if_fail (dl->path);
dl->mtp_client = mtp_client_get_file (url, dl->path, file_func, dl);
}
/* Launch if we have it */
else if (dl->path && dl->have_file)
{
GURL* url;
url = gnet_url_new_fields ("file", "", 0, dl->path);
ggui_activate (url);
gnet_url_delete (url);
}
/* Otherwise, we are in the process of downloading - ignore */
download_update (dl);
}
void
ggui_download_cancel (const GURL* url)
{
Download* dl;
int row;
g_return_if_fail (url);
/* Get download */
dl = download_get (url);
if (!dl)
return;
/* Remove from list */
row = gtk_clist_find_row_from_data (download_clist, dl);
gtk_clist_remove (download_clist, row);
/* Delete */
mtp_client_delete (dl->mtp_client);
dl->mtp_client = NULL;
}
gint /* -1 not trans, -2 = down */
ggui_download_get_percent_complete (const GURL* url)
{
Download* dl;
g_return_val_if_fail (url, -1);
dl = download_get (url);
if (!dl)
return -1;
if (dl->have_file)
return 100;
if (!dl->mtp_client) /* !dl->have_file */
return -2;
/* Otherwise, we are downloading the file */
if (dl->mtp_client->length == 0)
return 100;
return 100.0 * (((gdouble) dl->mtp_client->offset) /
((gdouble) dl->mtp_client->length));
}
/* ******************** */
static Download*
download_new (const GURL* url, const gchar* p, const GSHA* sha)
{
gchar* path;
MtpClient* mtp_client;
Download* dl;
gchar* texts[2] = {NULL, NULL};
gint row;
g_return_val_if_fail (url, NULL);
/* Get path */
path = get_path (p, gnet_url_basename(url));
g_return_val_if_fail (path, NULL);
/* Create MTP client */
mtp_client = mtp_client_get_file (url, path, file_func, NULL);
g_return_val_if_fail (mtp_client, NULL);
/* Create Download */
dl = g_new0(Download, 1);
dl->url = gnet_url_clone (url);
dl->path = path;
if (sha) dl->sha = gnet_sha_clone (sha);
dl->mtp_client = mtp_client;
mtp_client->user_data = dl;
g_hash_table_insert (url_to_dl, dl->url, dl);
/* Add to clist */
row = gtk_clist_prepend (download_clist, texts);
gtk_clist_set_row_data (download_clist, row, dl);
return dl;
}
static gchar*
get_path (const gchar* p, const gchar* name)
{
gchar* dir;
gchar* path;
g_return_val_if_fail (name && *name, NULL);
g_return_val_if_fail (jm_home, NULL);
g_return_val_if_fail (is_okfilename(name), NULL);
if (p && *p == '/' && G_DIR_SEPARATOR == '/' && is_okpathname(p))
dir = g_strconcat (jm_home, p, NULL);
else
dir = g_strdup (jm_home);
if (!mkdirr(dir, 0700))
{
g_warning ("Could not create directory %s: %s\n", dir, strerror(errno));
g_free (dir);
return NULL;
}
/* Create the final path name. Add directory separator if
needed. */
if (*dir && dir[strlen(dir) - 1] == G_DIR_SEPARATOR)
path = g_strconcat (dir, name, NULL);
else
path = g_strconcat (dir, G_DIR_SEPARATOR_S, name, NULL);
g_free (dir);
return path;
}
static void
file_func (MtpClient* client, MtpClientStatus status, gpointer user_data)
{
Download* dl = (Download*) user_data;
g_return_if_fail (dl);
if (status == MTP_CLIENT_STATUS_OK)
{
gint row;
if (dl->sha && client->sha)
{
gchar shastr1[2 * GNET_SHA_HASH_LENGTH + 1];
gchar shastr2[2 * GNET_SHA_HASH_LENGTH + 1];
gnet_sha_copy_string (dl->sha, shastr1);
shastr1[2 * GNET_SHA_HASH_LENGTH] = '\0';
GGUIP (8, "SHA CHECK1 %s\n", shastr1);
gnet_sha_copy_string (client->sha, shastr2);
shastr2[2 * GNET_SHA_HASH_LENGTH] = '\0';
GGUIP (8, "SHA CHECK2 %s\n", shastr2);
}
/* Check SHA */
if (dl->sha && client->sha && !gnet_sha_equal (dl->sha, client->sha))
{
ggui_show_message ("warning", _("SHA mismatch: %s may be corrupted."),
dl->path);
}
/* Remove from clist */
row = gtk_clist_find_row_from_data (download_clist, dl);
gtk_clist_remove (download_clist, row);
/* Delete client */
mtp_client_delete (client);
dl->mtp_client = NULL;
dl->have_file = TRUE;
download_update (dl);
}
else if (status == MTP_CLIENT_STATUS_UPDATE)
{
download_update (dl);
}
else
{
mtp_client_delete (client);
dl->mtp_client = NULL;
download_update (dl);
}
}
static void
download_update (Download* dl)
{
gint percent;
gint row;
gchar* name;
gchar* str = NULL;
GdkPixmap* pixmap;
GdkBitmap* mask;
g_return_if_fail (dl);
/* Update file part */
ggui_file_update (dl->url);
ggui_search_update (dl->url);
row = gtk_clist_find_row_from_data (download_clist, dl);
if (row < 0) /* Ignore if no longer in clist */
return;
name = gnet_url_basename (dl->url);
g_return_if_fail (name);
percent = ggui_download_get_percent_complete (dl->url);
if (percent >= 0 && dl->mtp_client)
{
gchar* fraction;
fraction = filesize_fraction_to_string(dl->mtp_client->offset,
dl->mtp_client->length);
str = g_strdup_printf("%d%% (%s)", percent, fraction);
g_free (fraction);
}
ggui_get_pixmaps (dl->url, &pixmap, &mask);
ggui_clist_update_row2 (download_clist, row, name, str, pixmap, mask);
g_free (str);
}
void
download_delete (Download* dl)
{
g_return_if_fail (dl);
gnet_url_delete (dl->url);
g_free (dl->path);
gnet_sha_delete (dl->sha);
mtp_client_delete (dl->mtp_client);
g_free (dl);
}
/* **************************************** */
void
on_download_select_row (GtkCList* list, gint row, gint column, GdkEvent* event)
{
selected_dl = (Download*) gtk_clist_get_row_data(list, row);
}
void
on_download_unselect_row (GtkCList* list, gint row, gint column, GdkEvent* event)
{
selected_dl = NULL;
}
/* Return TRUE if we handle event; FALSE to pass on */
gint
on_download_button_press (GtkCList* list, GdkEventButton *event, gpointer user_data)
{
gint row, column;
gboolean rv = FALSE;
/* We don't handle double-clicks */
/* User right-clicked - show context menu */
if (event->type == GDK_BUTTON_PRESS && event->button == 3)
{
GList* i;
/* Select the clicked row, if there is one */
if (gtk_clist_get_selection_info (list, event->x, event->y, &row, &column))
gtk_clist_select_row (list, row, 0);
else
gtk_clist_unselect_all (list);
/* Hide everything */
for (i = GTK_MENU_SHELL(download_popup)->children; i != NULL; i = i->next)
{
GtkWidget* menu_item = GTK_WIDGET(i->data);
g_return_val_if_fail (GTK_MENU_ITEM(menu_item) != NULL, FALSE);
gtk_widget_hide(menu_item);
}
/* Build the menu */
if (selected_dl)
{
update_items (NULL);
gtk_menu_popup (GTK_MENU(download_popup), NULL, NULL,
NULL, NULL, 3, event->time);
}
rv = TRUE;
}
return rv;
}
void
ggui_download_show_toolbar (void)
{
/* Add everything to the toolbar */
toolbar_append (IT_CANCEL_DOWNLOAD, down_page_pixmap, down_page_mask,
_("Cancel download"), on_download_cancel_download);
/* Update the toolbar */
update_toolbar();
}
static void
toolbar_append (Item item, GdkPixmap* gpixmap, GdkBitmap* gbitmap,
const gchar* tooltip, void* func)
{
GtkPixmap* pixmap;
GtkWidget* widget;
pixmap = (GtkPixmap*) gtk_pixmap_new (gpixmap, gbitmap);
g_return_if_fail (pixmap);
widget = gtk_toolbar_append_item (jmw_toolbar, NULL,
tooltip, "private",
GTK_WIDGET(pixmap),
(GtkSignalFunc) func, NULL);
g_return_if_fail (widget);
gtk_object_set_user_data (GTK_OBJECT(widget), (gpointer) item);
}
static void
update_toolbar (void)
{
GList* kids;
GList* i;
/* Hide all */
kids = gtk_container_children (GTK_CONTAINER(jmw_toolbar));
for (i = kids; i != NULL; i = i->next)
gtk_widget_set_sensitive (GTK_WIDGET(i->data), FALSE);
/* Update toolbar */
update_items (kids);
g_list_free (kids);
}
/* **************************************** */
static const char* item_mi_name[] =
{
"download_cancel_download_mi",
"separator1",
"properties_mi",
};
static void
update_items (void* widget)
{
if (selected_dl)
{
if (selected_dl->mtp_client)
show_item (widget, IT_CANCEL_DOWNLOAD);
show_item (widget, IT_SEPARATOR1);
show_item (widget, IT_PROPERTIES);
}
}
static void
show_item (void* widget, Item it)
{
if (!widget) /* Popup menu */
{
GtkWidget* menu_item = glade_xml_get_widget(menu_xml, item_mi_name[it]);
g_return_if_fail (menu_item != NULL);
gtk_widget_show(menu_item);
}
else /* Toolbar */
{
GList* kids = (GList*) widget;
GList* i;
for (i = kids; i != NULL; i = i->next)
{
if (it == (guint) gtk_object_get_user_data(GTK_OBJECT(i->data)))
{
gtk_widget_set_sensitive (GTK_WIDGET(i->data), TRUE);
break;
}
}
}
}
POPUPFUNC(on_download_cancel_download)
{
g_return_if_fail (selected_dl);
ggui_download_cancel (selected_dl->url);
}
POPUPFUNC(on_download_properties)
{
g_return_if_fail (selected_dl);
ggui_properties_show (selected_dl->url);
}
syntax highlighted by Code2HTML, v. 0.9.1