/* rgfetchprogress.cc * * Copyright (c) 2000, 2001 Conectiva S/A * 2002, 2003 Michael Vogt * * Author: Alfredo K. Kojima * Michael Vogt * * 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 "config.h" #include #include #include #include #include "rgfetchprogress.h" #include "rguserdialog.h" #include "rgmisc.h" #include #include #include #include #include "i18n.h" enum { DLDone = -1, DLQueued = -2, DLFailed = -3, DLHit = -4 }; enum { FETCH_PIXMAP_COLUMN, FETCH_SIZE_COLUMN, FETCH_DESCR_COLUMN, FETCH_URL_COLUMN, N_FETCH_COLUMNS }; static const int COLUMN_PERCENT_WIDTH=100; static const int COLUMN_PERCENT_HEIGHT=18; bool RGFetchProgress::close() { stopDownload(NULL, this); return TRUE; } RGFetchProgress::RGFetchProgress(RGWindow *win) : RGGladeWindow(win, "fetch"), _cursorDirty(false), _sock(NULL) { GtkCellRenderer *renderer; GtkTreeViewColumn *column; _mainProgressBar = glade_xml_get_widget(_gladeXML, "progressbar_download"); assert(_mainProgressBar); _table = glade_xml_get_widget(_gladeXML, "treeview_fetch"); _tableListStore = gtk_list_store_new(N_FETCH_COLUMNS, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING); gtk_tree_view_set_model(GTK_TREE_VIEW(_table), GTK_TREE_MODEL(_tableListStore)); /* percent column */ renderer = gtk_cell_renderer_pixbuf_new(); column = gtk_tree_view_column_new_with_attributes(_("Status"), renderer, "pixbuf", FETCH_PIXMAP_COLUMN, NULL); gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED); gtk_tree_view_column_set_fixed_width(column, COLUMN_PERCENT_WIDTH); _statusColumn = column; _statusRenderer = renderer; gtk_tree_view_append_column(GTK_TREE_VIEW(_table), column); /* size */ renderer = gtk_cell_renderer_text_new(); column = gtk_tree_view_column_new_with_attributes(_("Size"), renderer, "text", FETCH_SIZE_COLUMN, NULL); gtk_tree_view_append_column(GTK_TREE_VIEW(_table), column); /* descr */ renderer = gtk_cell_renderer_text_new(); column = gtk_tree_view_column_new_with_attributes(_("Package"), renderer, "text",FETCH_DESCR_COLUMN, NULL); gtk_tree_view_append_column(GTK_TREE_VIEW(_table), column); /* url */ renderer = gtk_cell_renderer_text_new(); column = gtk_tree_view_column_new_with_attributes(_("URI"), renderer, "text", FETCH_URL_COLUMN, NULL); gtk_tree_view_append_column(GTK_TREE_VIEW(_table), column); glade_xml_signal_connect_data(_gladeXML, "on_button_cancel_clicked", G_CALLBACK(stopDownload), this); PangoContext *context = gdk_pango_context_get(); _layout = pango_layout_new(context); // check if we should run embedded somewhere // we need to make the window show before we obtain the _gc int id = _config->FindI("Volatile::PlugProgressInto", -1); //cout << "Plug ID: " << id << endl; if (id > 0) { gtk_widget_hide(glade_xml_get_widget(_gladeXML, "window_fetch")); GtkWidget *vbox = glade_xml_get_widget(_gladeXML, "vbox_fetch"); _sock = gtk_plug_new(id); gtk_widget_reparent(vbox, _sock); gtk_widget_show_all(_sock); _win = _sock; } show(); GtkStyle *style = gtk_widget_get_style(_win); _font = style->font_desc; _gc = style->white_gc; _barGC = style->bg_gc[0]; _textGC = style->black_gc; // emit a signal if the user changed the cursor g_signal_connect(G_OBJECT(_table), "cursor-changed", G_CALLBACK(cursorChanged), this); // emit a signal if the user changed the cursor GtkWidget *expander = glade_xml_get_widget(_gladeXML, "expander"); assert(expander); g_signal_connect (expander, "notify::expanded", G_CALLBACK (expanderActivate), this); } void RGFetchProgress::expanderActivate(GObject *object, GParamSpec *param_spec, gpointer data) { GtkExpander *expander = GTK_EXPANDER (object); RGFetchProgress *me = (RGFetchProgress*)data; GtkWidget *win = glade_xml_get_widget(me->_gladeXML, "window_fetch"); if (gtk_expander_get_expanded (expander)) gtk_window_set_resizable(GTK_WINDOW(win),TRUE); else gtk_window_set_resizable(GTK_WINDOW(win), FALSE); } void RGFetchProgress::cursorChanged(GtkTreeView *self, void *data) { //cout << "cursor-changed" << endl; RGFetchProgress *me = (RGFetchProgress *)data; me->_cursorDirty=true; } void RGFetchProgress::setDescription(string mainText, string secondText) { gtk_window_set_title(GTK_WINDOW(_win), mainText.c_str()); gchar *str = g_strdup_printf("%s \n\n%s", mainText.c_str(), secondText.c_str()); gtk_label_set_markup(GTK_LABEL(glade_xml_get_widget(_gladeXML, "label_description")), str); g_free(str); } bool RGFetchProgress::MediaChange(string Media, string Drive) { gchar *msg; msg = g_strdup_printf(_("Please insert the disk labeled:\n%s\nin drive %s"), Media.c_str(), Drive.c_str()); RGUserDialog userDialog(this); _cancelled = !userDialog.proceed(msg); RGFlushInterface(); g_free(msg); return true; #if 0 // this code can be used when apt fixed ubuntu #2281 (patch pending) bool res = !userDialog.proceed(msg); //cout << "Media change " << res << endl; RGFlushInterface(); g_free(msg); if(res) { return false; } else { Update = true; return true; } #endif } void RGFetchProgress::updateStatus(pkgAcquire::ItemDesc & Itm, int status) { //cout << "void RGFetchProgress::updateStatus()" << endl; if (Itm.Owner->ID == 0) { Item item; item.descr = Itm.ShortDesc; item.uri = Itm.Description; item.size = string(SizeToStr(Itm.Owner->FileSize)); item.status = status; _items.push_back(item); Itm.Owner->ID = _items.size(); refreshTable(Itm.Owner->ID - 1, true); } else if (_items[Itm.Owner->ID - 1].status != status) { _items[Itm.Owner->ID - 1].status = status; refreshTable(Itm.Owner->ID - 1, false); } } void RGFetchProgress::IMSHit(pkgAcquire::ItemDesc & Itm) { //cout << "void RGFetchProgress::IMSHit(pkgAcquire::ItemDesc &Itm)" << endl; updateStatus(Itm, DLHit); RGFlushInterface(); } void RGFetchProgress::Fetch(pkgAcquire::ItemDesc & Itm) { updateStatus(Itm, DLQueued); RGFlushInterface(); } void RGFetchProgress::Done(pkgAcquire::ItemDesc & Itm) { updateStatus(Itm, DLDone); RGFlushInterface(); } void RGFetchProgress::Fail(pkgAcquire::ItemDesc & Itm) { if (Itm.Owner->Status == pkgAcquire::Item::StatIdle) return; updateStatus(Itm, DLFailed); RGFlushInterface(); } bool RGFetchProgress::Pulse(pkgAcquire * Owner) { //cout << "RGFetchProgress::Pulse(pkgAcquire *Owner)" << endl; pkgAcquireStatus::Pulse(Owner); for (pkgAcquire::Worker * I = Owner->WorkersBegin(); I != 0; I = Owner->WorkerStep(I)) { if (I->CurrentItem == 0) continue; if (I->TotalSize > 0) updateStatus(*I->CurrentItem, long (double (I->CurrentSize * 100.0) / double (I->TotalSize))); else updateStatus(*I->CurrentItem, 100); } float percent = long (double ((CurrentBytes + CurrentItems) * 100.0) / double (TotalBytes + TotalItems)); // work-around a stupid problem with libapt if(CurrentItems == TotalItems) percent=100.0; gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(_mainProgressBar), percent / 100.0); unsigned long ETA = (unsigned long)((TotalBytes - CurrentBytes) / CurrentCPS); long i = CurrentItems < TotalItems ? CurrentItems + 1 : CurrentItems; gchar *s; if (CurrentCPS != 0 && ETA != 0) { s = g_strdup_printf(_("Download rate: %s/s - %s remaining"), SizeToStr(CurrentCPS).c_str(), TimeToStr(ETA).c_str()); gtk_label_set_text(GTK_LABEL(glade_xml_get_widget(_gladeXML, "label_eta")),s); g_free(s); } else { gtk_label_set_text(GTK_LABEL(glade_xml_get_widget(_gladeXML, "label_eta")),_("Download rate: unknown")); } s = g_strdup_printf(_("Downloading file %li of %li"), i, TotalItems); gtk_progress_bar_set_text(GTK_PROGRESS_BAR(_mainProgressBar), s); g_free(s); RGFlushInterface(); return !_cancelled; } void RGFetchProgress::Start() { //cout << "RGFetchProgress::Start()" << endl; pkgAcquireStatus::Start(); _cancelled = false; show(); RGFlushInterface(); } void RGFetchProgress::Stop() { //cout << "RGFetchProgress::Stop()" << endl; RGFlushInterface(); if(_sock != NULL) { gtk_widget_destroy(_sock); } else { hide(); } pkgAcquireStatus::Stop(); //FIXME: this needs to be handled in a better way (gtk-2 maybe?) sleep(1); // this sucks, but if ommited, the window will not always // closed (e.g. when a package is only deleted) RGFlushInterface(); } void RGFetchProgress::stopDownload(GtkWidget *self, void *data) { RGFetchProgress *me = (RGFetchProgress *) data; me->_cancelled = true; } GdkPixmap *RGFetchProgress::statusDraw(int width, int height, int status) { int x, y; char *str = ""; GdkPixmap *pix; int px, pw; pix = gdk_pixmap_new(_win->window, width, height, -1); px = 0; pw = status * width / 100; if (status < 0) { if (status == DLDone || status == DLHit) gdk_draw_rectangle(pix, _barGC, TRUE, px, 0, pw, height); else gdk_draw_rectangle(pix, _gc, TRUE, px, 0, pw, height); switch (status) { case DLQueued: str = _("Queued"); break; case DLDone: str = _("Done"); break; case DLHit: str = _("Hit"); break; case DLFailed: str = _("Failed"); break; } } else { static char buf[16]; gdk_draw_rectangle(pix, _barGC, TRUE, px, 0, pw, height); gdk_draw_rectangle(pix, _gc, TRUE, px + pw, 0, width - pw - 2, height); snprintf(buf, sizeof(buf), "%d%%", status); str = buf; } pango_layout_set_font_description(_layout, _font); pango_layout_set_text(_layout, str, -1); pango_layout_get_pixel_size(_layout, &x, &y); x = (width - x) / 2; gdk_draw_layout(pix, _textGC, x, 0, _layout); return pix; } void RGFetchProgress::refreshTable(int row, bool append) { //cout << "RGFetchProgress::refreshTable() " << row << endl; GtkTreeIter iter; static GdkPixmap *pix = NULL; static GdkPixbuf *buf = NULL; int w, h; // unref pix first (they start with a usage count of 1 // why not unref'ing it after adding in the table? -- niemeyer if (pix != NULL) gdk_pixmap_unref(pix); if (buf != NULL) gdk_pixbuf_unref(buf); w = COLUMN_PERCENT_WIDTH; //gtk_tree_view_column_get_width(_statusColumn); h = COLUMN_PERCENT_HEIGHT; // FIXME: height -> get it from somewhere pix = statusDraw(w, h, _items[row].status); buf = gdk_pixbuf_get_from_drawable(NULL, pix, NULL, 0, 0, 0, 0, w, h); GtkTreePath *path; if (append == true) { gtk_list_store_insert(_tableListStore, &iter, row); path = gtk_tree_path_new(); gtk_tree_path_append_index(path, row); //gtk_tree_view_set_cursor(GTK_TREE_VIEW(_table), path, NULL, false); if(!_cursorDirty) gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(_table), path, NULL, TRUE, 0.0, 0.0); gtk_tree_path_free(path); // can't we use the iterator here? } else { gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(_tableListStore), &iter, NULL, row); } gtk_list_store_set(_tableListStore, &iter, FETCH_PIXMAP_COLUMN, buf, FETCH_SIZE_COLUMN, _items[row].size.c_str(), FETCH_DESCR_COLUMN, _items[row].descr.c_str(), FETCH_URL_COLUMN, _items[row].uri.c_str(), -1); path = gtk_tree_model_get_path(GTK_TREE_MODEL(_tableListStore), &iter); if(!_cursorDirty) gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(_table), path, NULL, TRUE, 0.0, 0.0); gtk_tree_path_free(path); } // vim:sts=4:sw=4