summaryrefslogtreecommitdiff
path: root/src/ui_download_manager.h
blob: cd1deea0f7bc21db026c6d751b3518e7088d482f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268

// ui_download_manager.h                           -*-c++-*-
//
//   Copyright (C) 2005, 2007-2009 Daniel Burrows
//
//   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; see the file COPYING.  If not, write to
//   the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
//   Boston, MA 02111-1307, USA.
//

#ifndef UI_DOWNLOAD_MANAGER_H
#define UI_DOWNLOAD_MANAGER_H

#include "download_thread.h"

#include <apt-pkg/acquire.h>
#include <apt-pkg/progress.h>

#include <cwidget/generic/util/ref_ptr.h>

#include <generic/apt/download_manager.h>

#include <generic/util/refcounted_base.h>

#include <sigc++/signal.h>
#include <sigc++/trackable.h>

#include <boost/shared_ptr.hpp>

/** \brief  Glue code to go between the UI and the download manager/thread stuff
 * 
 *  \file ui_download_manager.h
 */

class download_signal_log;
class download_thread;
namespace cwidget
{
  namespace widgets
  {
    class widget;
  }
}

/** A base class for objects that provide the OpProgress interface and
 *  that can be refcounted.
 *
 *  Note that this class is not abstract: you can instantiate it to
 *  get a NOP progress object.
 */
class refcounted_progress : public OpProgress,
			    virtual public aptitude::util::refcounted_base_threadsafe
{
protected:
  refcounted_progress()
  {
  }

public:
  static cwidget::util::ref_ptr<refcounted_progress> create()
  {
    return new refcounted_progress;
  }

  static std::pair<cwidget::util::ref_ptr<refcounted_progress>,
		   sigc::slot0<void> >
  make()
  {
    return std::make_pair(create(), sigc::slot0<void>());
  }
};
typedef cwidget::util::ref_ptr<refcounted_progress> refcounted_progress_ref;

/** \brief A progress bar paired with a slot that tells
 *  us how to destroy it.
 */
typedef std::pair<refcounted_progress_ref,
		  sigc::slot0<void> > progress_with_destructor;

/** \brief A slot that creates a progress bar and
 *  tells us how to hide it.
 *
 *  The first returned value is a new progress reporting
 *  object; the second value is a slot to be invoked when
 *  any widget associated with the progress object
 *  should be cleaned up.
 */
typedef sigc::slot0<progress_with_destructor>
make_refcounted_progress_slot;

/** Represents the UI end of a download process.  This object
 *  completely handles its own memory management -- you don't have to
 *  delete it and you shouldn't try.
 */
class ui_download_manager : public sigc::trackable
{
  /** Used to indicate that a download was cancelled. */
  class aborter : public sigc::trackable
  {
    bool aborted;
  public:
    aborter() : aborted(false)
    {
    }

    void abort()
    {
      aborted = true;
    }

    bool get_aborted()
    {
      return aborted;
    }
  };

  boost::shared_ptr<download_manager> manager;

  aborter abort_state;

  download_signal_log *log;

  background_status *st;

  // Used to hide the details of how we make a progress bar.  Invoked
  // every time the ui_download_manager needs a progress bar to do
  // something.
  make_refcounted_progress_slot make_progress_bar;

  // How to post a thunk to the main thread.
  post_thunk_func post_thunk;

  // Wrapper so we can use generic refcounted stuff.
  //
  // All we want is a pointer that will drop a reference on the target
  // when we destroy it.
  //
  // This seems a little icky; it seems like there should be a better
  // way of handling these lifetime issues.
  template<typename T>
  class generic_refcounted : public aptitude::util::refcounted_base_threadsafe
  {
    cwidget::util::ref_ptr<T> ptr;

  public:
    generic_refcounted(const cwidget::util::ref_ptr<T> &_ptr)
      : ptr(_ptr)
    {
    }
  };

  /** Used to keep the download status widget alive until the download
   *  completes.
   */
  cwidget::util::ref_ptr<aptitude::util::refcounted_base_threadsafe> download_status;

  /** \brief A progress object, used to display progrss in done().
   *
   *  This is stored here to handle lifetime issues sensibly: we
   *  create it in done() and free it in finish_done(), and storing
   *  refcounted values in slot bindings is dangerous.
   *
   *  \todo Would it be reasonable to somehow just use two progress
   *  bars instead of passing one around?  The current scheme is the
   *  result of blindly refactoring the old done() and seems likely to
   *  become unmaintainable.
   */
  refcounted_progress_ref done_progress;

  /** A thunk that makes done_progress invisible and prepares
   *  to free its memory.
   */
  sigc::slot0<void> done_progress_destructor;

  void done(download_thread *, pkgAcquire::RunResult res);

  /** \brief Finishes the work of done() after the post-download
   *  actions of the download manager have run.
   *
   *  \param run_res  The result of download_manager::finish().
   */
  void finish_done(download_manager::result run_res);

public:
  /** \brief Create a ui_download_manager.
   *
   *  \param _manager The download_manager object to wrap.
   *
   *  \param _log     How to report the download status (the
   *                  signals will be invoked in a foreground
   *                  thread).
   *
   *  \param _download_status   An arbitrary object that
   *                            should be kept around until
   *                            the download completes (its
   *                            references will be adjusted
   *                            in the foreground thread).
   *
   *  \param _make_progress_bar  A callback that will be invoked
   *                             whenever the download process
   *                             needs a fresh progress bar.
   *
   *  param _post_thunk          A callback that schedules a
   *                             thunk for execution in the
   *                             foreground thread.
   */
  template<typename T>
  ui_download_manager(const boost::shared_ptr<download_manager> &_manager,
		      download_signal_log *_log,
		      const cwidget::util::ref_ptr<T> &_download_status,
		      const make_refcounted_progress_slot &_make_progress_bar,
		      post_thunk_func _post_thunk)
    : manager(_manager),
      abort_state(),
      log(_log),
      st(new background_status(_log, _post_thunk)),
      make_progress_bar(_make_progress_bar),
      post_thunk(_post_thunk),
      download_status(new generic_refcounted<T>(_download_status))
  {
  }

  ~ui_download_manager();

  /** \brief Invoke this method if the download is cancelled.
   *
   *  This doesn't cancel the download itself, but it changes
   *  the flow-of-control appropriately: for instance, dpkg
   *  won't be invoked for an aborted install run.
   */
  void aborted()
  {
    abort_state.abort();
  }

  void start();

  /** \brief A signal emitted when the download is about to start. */
  sigc::signal<void> download_starts;

  /** \brief A signal emitted when the download finishes,
   *  after Complete() is invoked on the log object.
   */
  sigc::signal<void> download_stops;

  /** \brief A signal emitted when the download and any post-download
   *  actions are finished.
   *
   *  The parameter is \b true if the download was sucessful and \b
   *  false otherwise.  The signal is always emitted in the foreground
   *  thread.
   */
  sigc::signal<void, bool> download_complete;
};


#endif