source: projects/synaptic/trunk/gtk/rgfetchprogress.cc @ 280

Revision 280, 13.3 KB checked in by yasumichi, 15 years ago (diff)

first import

Line 
1/* rgfetchprogress.cc
2 *
3 * Copyright (c) 2000, 2001 Conectiva S/A
4 *               2002, 2003 Michael Vogt <mvo@debian.org>
5 *
6 * Author: Alfredo K. Kojima <kojima@conectiva.com.br>
7 *         Michael Vogt <mvo@debian.org>
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as
11 * published by the Free Software Foundation; either version 2 of the
12 * License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
22 * USA
23 */
24
25#include "config.h"
26
27#include <apt-pkg/acquire-item.h>
28#include <apt-pkg/acquire-worker.h>
29#include <apt-pkg/strutl.h>
30#include <apt-pkg/error.h>
31
32#include "rgfetchprogress.h"
33#include "rguserdialog.h"
34#include "rgmisc.h"
35
36#include <stdio.h>
37#include <pango/pango.h>
38#include <gtk/gtk.h>
39#include <cassert>
40
41#include "i18n.h"
42
43enum {
44   DLDone = -1,
45   DLQueued = -2,
46   DLFailed = -3,
47   DLHit = -4
48};
49
50enum {
51   FETCH_PIXMAP_COLUMN,
52   FETCH_SIZE_COLUMN,
53   FETCH_DESCR_COLUMN,
54   FETCH_URL_COLUMN,
55   N_FETCH_COLUMNS
56};
57
58static const int COLUMN_PERCENT_WIDTH=100;
59static const int COLUMN_PERCENT_HEIGHT=18;
60
61
62bool RGFetchProgress::close()
63{
64   stopDownload(NULL, this);
65   
66   return TRUE;
67}
68
69RGFetchProgress::RGFetchProgress(RGWindow *win)
70   : RGGladeWindow(win, "fetch"), _cursorDirty(false), _sock(NULL)
71{
72   GtkCellRenderer *renderer;
73   GtkTreeViewColumn *column;
74
75   _mainProgressBar = glade_xml_get_widget(_gladeXML, "progressbar_download");
76   assert(_mainProgressBar);
77
78   _table = glade_xml_get_widget(_gladeXML, "treeview_fetch");
79   _tableListStore = gtk_list_store_new(N_FETCH_COLUMNS,
80                                        GDK_TYPE_PIXBUF,
81                                        G_TYPE_STRING, G_TYPE_STRING, 
82                                        G_TYPE_STRING);
83   gtk_tree_view_set_model(GTK_TREE_VIEW(_table),
84                           GTK_TREE_MODEL(_tableListStore));
85
86   /* percent column */
87   renderer = gtk_cell_renderer_pixbuf_new();
88   column = gtk_tree_view_column_new_with_attributes(_("Status"), renderer,
89                                                     "pixbuf",
90                                                     FETCH_PIXMAP_COLUMN,
91                                                     NULL);
92   gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
93   gtk_tree_view_column_set_fixed_width(column, COLUMN_PERCENT_WIDTH);
94   _statusColumn = column;
95   _statusRenderer = renderer;
96   gtk_tree_view_append_column(GTK_TREE_VIEW(_table), column);
97
98   /* size */
99   renderer = gtk_cell_renderer_text_new();
100   column = gtk_tree_view_column_new_with_attributes(_("Size"), renderer,
101                                                     "text", FETCH_SIZE_COLUMN,
102                                                     NULL);
103   gtk_tree_view_append_column(GTK_TREE_VIEW(_table), column);
104
105   /* descr */
106   renderer = gtk_cell_renderer_text_new();
107   column = gtk_tree_view_column_new_with_attributes(_("Package"), renderer,
108                                                     "text",FETCH_DESCR_COLUMN,
109                                                     NULL);
110   gtk_tree_view_append_column(GTK_TREE_VIEW(_table), column);
111
112   /* url */
113   renderer = gtk_cell_renderer_text_new();
114   column = gtk_tree_view_column_new_with_attributes(_("URI"), renderer,
115                                                     "text", FETCH_URL_COLUMN, 
116                                                     NULL);
117   gtk_tree_view_append_column(GTK_TREE_VIEW(_table), column);
118
119
120   glade_xml_signal_connect_data(_gladeXML,
121                                 "on_button_cancel_clicked",
122                                 G_CALLBACK(stopDownload), this);
123
124   PangoContext *context = gdk_pango_context_get();
125   _layout = pango_layout_new(context);
126
127   // check if we should run embedded somewhere
128   // we need to make the window show before we obtain the _gc
129   int id = _config->FindI("Volatile::PlugProgressInto", -1);
130   //cout << "Plug ID: " << id << endl;
131   if (id > 0) {
132      gtk_widget_hide(glade_xml_get_widget(_gladeXML, "window_fetch"));
133      GtkWidget *vbox = glade_xml_get_widget(_gladeXML, "vbox_fetch");
134      _sock =  gtk_plug_new(id);
135      gtk_widget_reparent(vbox, _sock);
136      gtk_widget_show_all(_sock);
137      _win = _sock;
138   } 
139   show();
140
141   GtkStyle *style = gtk_widget_get_style(_win);
142   _font = style->font_desc;
143   _gc = style->white_gc;
144   _barGC = style->bg_gc[0];
145   _textGC = style->black_gc;
146
147   // emit a signal if the user changed the cursor
148   g_signal_connect(G_OBJECT(_table), "cursor-changed", 
149                    G_CALLBACK(cursorChanged), this);
150
151   // emit a signal if the user changed the cursor
152   GtkWidget *expander = glade_xml_get_widget(_gladeXML, "expander");
153   assert(expander);
154   g_signal_connect (expander, "notify::expanded",
155                     G_CALLBACK (expanderActivate), this);
156
157}
158
159void RGFetchProgress::expanderActivate(GObject    *object,
160                                       GParamSpec *param_spec,
161                                       gpointer    data)
162{
163   GtkExpander *expander = GTK_EXPANDER (object);
164   RGFetchProgress *me = (RGFetchProgress*)data;
165
166   GtkWidget *win = glade_xml_get_widget(me->_gladeXML, "window_fetch");
167   if (gtk_expander_get_expanded (expander)) 
168      gtk_window_set_resizable(GTK_WINDOW(win),TRUE);
169   else 
170      gtk_window_set_resizable(GTK_WINDOW(win), FALSE);
171}
172
173
174void RGFetchProgress::cursorChanged(GtkTreeView *self, void *data)
175{
176   //cout << "cursor-changed" << endl;
177   RGFetchProgress *me = (RGFetchProgress *)data;
178   me->_cursorDirty=true;
179}
180
181void RGFetchProgress::setDescription(string mainText, string secondText)
182{
183   gtk_window_set_title(GTK_WINDOW(_win), mainText.c_str());
184   gchar *str = g_strdup_printf("<big><b>%s</b></big> \n\n%s",
185                                  mainText.c_str(), secondText.c_str());
186   gtk_label_set_markup(GTK_LABEL(glade_xml_get_widget(_gladeXML, "label_description")), str);
187                       
188
189   g_free(str);
190}
191
192bool RGFetchProgress::MediaChange(string Media, string Drive)
193{
194   gchar *msg;
195
196   msg = g_strdup_printf(_("Please insert the disk labeled:\n%s\nin drive %s"),
197                         Media.c_str(), Drive.c_str());
198
199   RGUserDialog userDialog(this);
200   _cancelled = !userDialog.proceed(msg);
201
202   RGFlushInterface();
203   g_free(msg);
204   return true;
205
206
207
208#if 0 // this code can be used when apt fixed ubuntu #2281 (patch pending)
209   bool res = !userDialog.proceed(msg);
210   //cout << "Media change " << res << endl;
211
212   RGFlushInterface();
213   g_free(msg);
214
215   if(res) {
216      return false;
217   } else {
218      Update = true;
219      return true;
220   }
221#endif
222}
223
224
225void RGFetchProgress::updateStatus(pkgAcquire::ItemDesc & Itm, int status)
226{
227   //cout << "void RGFetchProgress::updateStatus()" << endl;
228
229   if (Itm.Owner->ID == 0) {
230      Item item;
231      item.descr = Itm.ShortDesc;
232      item.uri = Itm.Description;
233      item.size = string(SizeToStr(Itm.Owner->FileSize));
234      item.status = status;
235      _items.push_back(item);
236      Itm.Owner->ID = _items.size();
237      refreshTable(Itm.Owner->ID - 1, true);
238   } else if (_items[Itm.Owner->ID - 1].status != status) {
239      _items[Itm.Owner->ID - 1].status = status;
240      refreshTable(Itm.Owner->ID - 1, false);
241   }
242}
243
244
245void RGFetchProgress::IMSHit(pkgAcquire::ItemDesc & Itm)
246{
247   //cout << "void RGFetchProgress::IMSHit(pkgAcquire::ItemDesc &Itm)" << endl;
248   updateStatus(Itm, DLHit);
249
250   RGFlushInterface();
251}
252
253
254void RGFetchProgress::Fetch(pkgAcquire::ItemDesc & Itm)
255{
256   updateStatus(Itm, DLQueued);
257
258   RGFlushInterface();
259}
260
261
262void RGFetchProgress::Done(pkgAcquire::ItemDesc & Itm)
263{
264   updateStatus(Itm, DLDone);
265
266   RGFlushInterface();
267}
268
269
270void RGFetchProgress::Fail(pkgAcquire::ItemDesc & Itm)
271{
272   if (Itm.Owner->Status == pkgAcquire::Item::StatIdle)
273      return;
274
275   updateStatus(Itm, DLFailed);
276
277   RGFlushInterface();
278}
279
280
281bool RGFetchProgress::Pulse(pkgAcquire * Owner)
282{
283   //cout << "RGFetchProgress::Pulse(pkgAcquire *Owner)" << endl;
284
285   pkgAcquireStatus::Pulse(Owner);
286
287   for (pkgAcquire::Worker * I = Owner->WorkersBegin(); I != 0;
288        I = Owner->WorkerStep(I)) {
289
290      if (I->CurrentItem == 0)
291         continue;
292
293      if (I->TotalSize > 0)
294         updateStatus(*I->CurrentItem,
295                      long (double (I->CurrentSize * 100.0) /
296                            double (I->TotalSize)));
297      else
298         updateStatus(*I->CurrentItem, 100);
299
300   }
301
302   float percent =
303      long (double ((CurrentBytes + CurrentItems) * 100.0) /
304            double (TotalBytes + TotalItems));
305   // work-around a stupid problem with libapt
306   if(CurrentItems == TotalItems)
307      percent=100.0;
308
309   gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(_mainProgressBar),
310                                 percent / 100.0);
311
312   unsigned long ETA =
313      (unsigned long)((TotalBytes - CurrentBytes) / CurrentCPS);
314   long i = CurrentItems < TotalItems ? CurrentItems + 1 : CurrentItems;
315   gchar *s;
316   if (CurrentCPS != 0 && ETA != 0) {
317      s = g_strdup_printf(_("Download rate: %s/s - %s remaining"),
318                          SizeToStr(CurrentCPS).c_str(),
319                          TimeToStr(ETA).c_str());
320      gtk_label_set_text(GTK_LABEL(glade_xml_get_widget(_gladeXML, "label_eta")),s);
321      g_free(s);
322   } else {
323      gtk_label_set_text(GTK_LABEL(glade_xml_get_widget(_gladeXML, "label_eta")),_("Download rate: unknown"));
324   }
325   s = g_strdup_printf(_("Downloading file %li of %li"), i, TotalItems);
326   gtk_progress_bar_set_text(GTK_PROGRESS_BAR(_mainProgressBar), s);
327   g_free(s);
328
329   RGFlushInterface();
330
331   return !_cancelled;
332}
333
334
335void RGFetchProgress::Start()
336{
337   //cout << "RGFetchProgress::Start()" << endl;
338   pkgAcquireStatus::Start();
339   _cancelled = false;
340   show();
341
342   RGFlushInterface();
343}
344
345
346void RGFetchProgress::Stop()
347{
348   //cout << "RGFetchProgress::Stop()" << endl;
349   RGFlushInterface();
350   if(_sock != NULL) {
351      gtk_widget_destroy(_sock);
352   } else {
353      hide();
354   }
355   pkgAcquireStatus::Stop();
356
357   //FIXME: this needs to be handled in a better way (gtk-2 maybe?)
358   sleep(1);                    // this sucks, but if ommited, the window will not always
359   // closed (e.g. when a package is only deleted)
360   RGFlushInterface();
361}
362
363
364
365void RGFetchProgress::stopDownload(GtkWidget *self, void *data)
366{
367   RGFetchProgress *me = (RGFetchProgress *) data;
368
369   me->_cancelled = true;
370}
371
372
373GdkPixmap *RGFetchProgress::statusDraw(int width, int height, int status)
374{
375   int x, y;
376   char *str = "";
377   GdkPixmap *pix;
378   int px, pw;
379
380   pix = gdk_pixmap_new(_win->window, width, height, -1);
381
382   px = 0;
383   pw = status * width / 100;
384
385   if (status < 0) {
386      if (status == DLDone || status == DLHit)
387         gdk_draw_rectangle(pix, _barGC, TRUE, px, 0, pw, height);
388      else
389         gdk_draw_rectangle(pix, _gc, TRUE, px, 0, pw, height);
390
391      switch (status) {
392         case DLQueued:
393            str = _("Queued");
394            break;
395         case DLDone:
396            str = _("Done");
397            break;
398         case DLHit:
399            str = _("Hit");
400            break;
401         case DLFailed:
402            str = _("Failed");
403            break;
404      }
405   } else {
406      static char buf[16];
407
408      gdk_draw_rectangle(pix, _barGC, TRUE, px, 0, pw, height);
409      gdk_draw_rectangle(pix, _gc, TRUE, px + pw, 0, width - pw - 2, height);
410
411      snprintf(buf, sizeof(buf), "%d%%", status);
412      str = buf;
413   }
414
415   pango_layout_set_font_description(_layout, _font);
416   pango_layout_set_text(_layout, str, -1);
417   pango_layout_get_pixel_size(_layout, &x, &y);
418   x = (width - x) / 2;
419   gdk_draw_layout(pix, _textGC, x, 0, _layout);
420
421   return pix;
422}
423
424
425void RGFetchProgress::refreshTable(int row, bool append)
426{
427   //cout << "RGFetchProgress::refreshTable() " << row << endl;
428   GtkTreeIter iter;
429   static GdkPixmap *pix = NULL;
430   static GdkPixbuf *buf = NULL;
431   int w, h;
432
433   // unref pix first (they start with a usage count of 1
434   // why not unref'ing it after adding in the table? -- niemeyer
435   if (pix != NULL)
436      gdk_pixmap_unref(pix);
437   if (buf != NULL)
438      gdk_pixbuf_unref(buf);
439
440   w = COLUMN_PERCENT_WIDTH;   //gtk_tree_view_column_get_width(_statusColumn);
441   h = COLUMN_PERCENT_HEIGHT; // FIXME: height -> get it from somewhere
442
443   pix = statusDraw(w, h, _items[row].status);
444   buf = gdk_pixbuf_get_from_drawable(NULL, pix, NULL, 0, 0, 0, 0, w, h);
445   GtkTreePath *path;
446   if (append == true) {
447      gtk_list_store_insert(_tableListStore, &iter, row);
448      path = gtk_tree_path_new();
449      gtk_tree_path_append_index(path, row);
450      //gtk_tree_view_set_cursor(GTK_TREE_VIEW(_table), path, NULL, false);
451      if(!_cursorDirty)
452         gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(_table),
453                                      path, NULL, TRUE, 0.0, 0.0);
454      gtk_tree_path_free(path);
455      // can't we use the iterator here?
456   } else {
457      gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(_tableListStore),
458                                    &iter, NULL, row);
459   }
460   gtk_list_store_set(_tableListStore, &iter,
461                      FETCH_PIXMAP_COLUMN, buf,
462                      FETCH_SIZE_COLUMN, _items[row].size.c_str(),
463                      FETCH_DESCR_COLUMN, _items[row].descr.c_str(),
464                      FETCH_URL_COLUMN, _items[row].uri.c_str(), -1);
465   path = gtk_tree_model_get_path(GTK_TREE_MODEL(_tableListStore), &iter);
466   if(!_cursorDirty)
467      gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(_table),
468                                   path, NULL, TRUE, 0.0, 0.0);
469   gtk_tree_path_free(path);
470}
471
472// vim:sts=4:sw=4
Note: See TracBrowser for help on using the repository browser.