summaryrefslogtreecommitdiff
path: root/src/gtk/resolver.h
blob: d5a8b99bdfab8793e65808d8bdf2066bfd3b1c52 (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
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
// -*-c++-*-

// resolver.h
//
//  Copyright 1999-2009 Daniel Burrows
//  Copyright 2008 Obey Arthur Liu
//
//  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 RESOLVER_H_
#define RESOLVER_H_

#undef OK
#include <gtkmm.h>
#include <libglademm/xml.h>

#include <generic/apt/apt.h>
#include <generic/apt/aptitude_resolver_universe.h>
#include <generic/apt/resolver_manager.h>
#include <generic/problemresolver/choice.h>
#include <generic/problemresolver/solution.h>
#include <generic/util/maybe.h>
#include <generic/util/util.h>

#include <gtk/tab.h>

namespace gui
{

  class ResolverColumns : public Gtk::TreeModel::ColumnRecord
  {
    public:
      Gtk::TreeModelColumn<pkgCache::PkgIterator> PkgIterator;
      Gtk::TreeModelColumn<pkgCache::VerIterator> VerIterator;
    Gtk::TreeModelColumn<Glib::ustring> ActionMarkup;
    Gtk::TreeModelColumn<bool> BgSet;
    Gtk::TreeModelColumn<Glib::ustring> BgColor;
    Gtk::TreeModelColumn<Glib::ustring> PreferenceIcon;
    Gtk::TreeModelColumn<Glib::ustring> PreferenceIconTooltip;
    Gtk::TreeModelColumn<maybe<generic_choice<aptitude_universe> > > Choice;

      ResolverColumns();
  };

  class AlreadyGeneratedSolutionColumns : public Gtk::TreeModel::ColumnRecord
  {
  public:
    // A column showing the index of the solution (as a string).
    Gtk::TreeModelColumn<Glib::ustring> Index;
    // The same information, as an integer (for internal use).
    Gtk::TreeModelColumn<int> IndexNum;
    // The main "markup" column.
    Gtk::TreeModelColumn<Glib::ustring> Markup;
    // The column storing the "tooltip" associated with each solution.
    Gtk::TreeModelColumn<Glib::ustring> TooltipMarkup;
    Gtk::TreeModelColumn<generic_solution<aptitude_universe> > Solution;

    AlreadyGeneratedSolutionColumns();
  };

  // Wrapper that adds automatic updates of resolver item states to
  // the resolver view.
  class ResolverView : public aptitude::util::refcounted_base_threadsafe
  {
  private:
    Gtk::TreeViewColumn * ActionMarkup;
    Gtk::TreeViewColumn * PreferenceIcon;

    Gtk::TreeView *view;
    ResolverColumns resolver_columns;

    // These backpointers track the current state of the store; they
    // are used to respond to signals from the resolver manager about
    // changes to reject/approve states.
    std::map<aptitude_resolver_version, Gtk::TreeModel::iterator> version_backpointers;
    std::map<aptitude_resolver_dep, Gtk::TreeModel::iterator> dep_backpointers;

    // Set up some common column properties.
    void set_column_properties(Gtk::TreeViewColumn *treeview_column,
			       int size);

    template<typename ColumnType>
    int append_column(const Glib::ustring &title,
		      Gtk::TreeViewColumn *out_treeview_column,
		      const Gtk::TreeModelColumn<ColumnType> &model_column,
		      int size);

    bool add_backpointer(const Gtk::TreeModel::Path &path,
			 const Gtk::TreeModel::iterator &iter);

    bool sanity_check_iter(const Gtk::TreeModel::iterator &iter) const;

    // Routines and structures that manage the accepted/rejected
    // states of rows; placed here so that the incremental update can
    // be a member function.

    /** \brief Information about how to display the user's preference
     *  about a package.
     */
    struct preference_info
    {
      Glib::ustring icon;
      Glib::ustring icon_tooltip;
      Glib::ustring background;
      bool background_set;

      preference_info()
	: icon(),
	  icon_tooltip(),
	  background(),
	  background_set(false)
      {
      }

      preference_info(const Glib::ustring &_icon,
		      const Glib::ustring &_icon_tooltip,
		      const Glib::ustring &_background,
		      bool _background_set)
	: icon(_icon),
	  icon_tooltip(_icon_tooltip),
	  background(_background),
	  background_set(_background_set)
      {
      }
    };
    preference_info get_preference_info(const generic_choice<aptitude_universe> &c,
					resolver_manager *manager) const;
    void set_preference_info(Gtk::TreeModel::Row &row,
			     const preference_info &pref_inf) const;
    void set_preference_info(Gtk::TreeModel::Row &row,
			     const generic_choice<aptitude_universe> &c,
			     resolver_manager *manager) const;

    // Used to iterate over the model when it's first set and update
    // its preference-information columns.
    bool update_preference_info(const Gtk::TreeModel::iterator &iter,
				resolver_manager *manager);

    ResolverView(Gtk::TreeView *view);
  public:
    /** \brief Create a new resolver view.
     *
     *  The given view is customized with the standard set of columns
     *  for a resolver-view.
     */
    static cwidget::util::ref_ptr<ResolverView> create(Gtk::TreeView *view);

    /** \brief Retrieve the column structure associated with this view. */
    const ResolverColumns &get_columns() const { return resolver_columns; }

    /** \brief Set the model displayed by this view.
     *
     *  \param model    The new tree-model to display.
     *  \param manager  The resolver-manager to use to flesh out
     *                  the model.
     *
     *  This routine updates the tree-view and builds the backpointer
     *  maps; it also fills in preference information columns.
     */
    void set_model(const Glib::RefPtr<Gtk::TreeModel> &model,
		   resolver_manager *manager);

    /** \brief Update the preference state of rows attached to the
     *  given version.
     */
    void update_version(const aptitude_resolver_version &version,
			resolver_manager *manager);

    /** \brief Update the preference state of rows attached to the
     *  given dependency.
     */
    void update_dep(const aptitude_resolver_dep &dep,
		    resolver_manager *manager);

    Gtk::TreeView *get_treeview() const { return view; }
  };

  class ResolverTab : public Tab
  {
    private:
      typedef generic_solution<aptitude_universe> aptitude_solution;

    // The resolver manager that we're using.
    resolver_manager *resolver;

    // True if we're using an internal resolver; false otherwise.
    bool using_internal_resolver;

    resolver_manager *get_resolver() const
    {
      return using_internal_resolver ? resolver : resman;
    }

    sigc::connection resolver_state_changed_connection;
    sigc::connection resolver_version_accept_reject_changed_connection;
    sigc::connection resolver_break_dep_accept_reject_changed_connection;

    cwidget::util::ref_ptr<ResolverView> solution_view;

    // The columns and tree-view for the list of previously generated
    // solutions.
    AlreadyGeneratedSolutionColumns already_generated_columns;
    Gtk::TreeView * already_generated_view;
    Glib::RefPtr<Gtk::ListStore> already_generated_model;

      Gtk::Label * pResolverStatus;
      Gtk::Button * pResolverApply;
    Gtk::Button *find_next_solution_button;

    // The top container for the "fixing upgrade..." message.  Used to
    // show and hide it.
    Gtk::Widget * resolver_fixing_upgrade_message;

    Gtk::ProgressBar * resolver_fixing_upgrade_progress_bar;
    Gtk::Label * resolver_fixing_upgrade_label;

    Gtk::RadioButton * pButtonGroupByAction;
    Gtk::RadioButton * pButtonShowExplanation;

    Gtk::Label * acceptreject_label;
    Gtk::ToggleButton * reject_button;
    Gtk::ToggleButton * no_preference_button;
    Gtk::ToggleButton * accept_button;

    // The solution, if any, that is currently displayed in the
    // solution pane.
    aptitude_solution displayed_solution;

    /** \brief Add a child of the given iterator and populate it from
     *  the given choice.
     */
    void append_choice(const Glib::RefPtr<Gtk::TreeStore> &store,
		       const cwidget::util::ref_ptr<ResolverView> &view,
		       const Gtk::TreeModel::Row &parent_row,
		       const generic_choice<aptitude_universe> &c) const;

    // These two routines manage the connections to the resolver and
    // other state that has to be thrown away and recreated when the
    // cache is reloaded.
    void handle_cache_closed();
    void setup_resolver_connections();

    /** \brief Create a new tree store and populate it with the given
     *  solution, rendered with actions collected by type.
     */
    Glib::RefPtr<Gtk::TreeStore> render_as_action_groups(const aptitude_solution &sol);

    /** \brief Create a new tree store and populate it with
     *  the given solution, rendered as a chronological explanation
     *  of each action.
     */
    Glib::RefPtr<Gtk::TreeStore> render_as_explanation(const aptitude_solution &sol);

    /** \brief Fill in a row in the list of generated solutions.
     *
     *  \param sol   The solution that the row represents.
     *  \param index The index of this solution.
     *  \param row   The row to fill in.
     */
    void render_already_generated_row(const aptitude_solution &sol,
				      int index,
				      Gtk::TreeModel::Row &row) const;

      std::string archives_text(const pkgCache::VerIterator &ver);
      std::string dep_targets(const pkgCache::DepIterator &start) const;
    std::string dep_text(const pkgCache::DepIterator &d) const;
    bool do_find_next_solution_enabled();
    bool do_find_next_solution_enabled_from_state(const resolver_manager::state &state);
    void do_find_next_solution();
      void do_apply_solution();

    /** \brief Update the solution pane from the currently selected
     *  solution in the solution list.
     *
     *  If there is no currently selected solution, the solution pane
     *  is always updated with an explanation of why; otherwise, it is
     *  only updated if the solution actually changed.
     *
     *  \param force_update   If true, the pane will be updated even if
     *                        there is no new solution.  Used to change
     *                        the view mode.
     */
    void update_solution_pane(bool force_update);

      /** \brief Updates the tab with the given resolver state.
       *
       *  \param force_update if \b true, the tree is rebuilt even if
       *  the current solution hasn't changed.
       */
    void update_from_state(const resolver_manager::state &state,
			   bool force_update);

      /** \brief Updates the tab with the current resolver state.
       *
       *  This is connected to the global state-changed signal; in
       *  functions that check or read the state before triggering an
       *  update, invoke update(state) instead to ensure
       *  consistency.
       *
       *  \param force_update if \b true, the tree is rebuilt even if
       *  the current solution hasn't changed.
       */
      void update(bool force_update);

    // Helper class used by update_reject_accept_buttons.
    class update_reject_accept_counter;

    void version_accept_reject_changed(const aptitude_resolver_version &ver);
    void break_dep_accept_reject_changed(const aptitude_resolver_dep &dep);

    // Updates the label by the reject/accept buttons and might change
    // the sensitivity of the buttons.  Invoked when the selection
    // state changes, when the resolver state changes, and when a new
    // solution is selected.
    void update_reject_accept_buttons();

    // We need to avoid responding to toggle messages while the
    // buttons are being updated; otherwise we could end up with
    // unexpected or inefficient behavior.  This Boolean value and the
    // accompanying RAII class are used to suppress toggle button
    // responses.
    bool toggle_signals_suppressed;
    class suppress_toggle_signals
    {
      ResolverTab &parent;
      bool old_toggle_signals_suppressed;

    public:
      suppress_toggle_signals(ResolverTab &_parent)
	: parent(_parent),
	  old_toggle_signals_suppressed(_parent.toggle_signals_suppressed)
      {
	parent.toggle_signals_suppressed = true;
      }

      ~suppress_toggle_signals()
      {
	parent.toggle_signals_suppressed = false;
      }
    };

    // Helpers, applied to each element in the selection to implement
    // the various action buttons.
    void do_reject_choice(const maybe<generic_choice<aptitude_universe> > &maybe_c);
    void do_no_preference_choice(const maybe<generic_choice<aptitude_universe> > &maybe_c);
    void do_accept_choice(const maybe<generic_choice<aptitude_universe> > &maybe_c);

    void reject_button_toggled();
    void no_preference_button_toggled();
    void accept_button_toggled();

    public:
      ResolverTab(const Glib::ustring &label);
    const cwidget::util::ref_ptr<ResolverView> &get_solution_view() const { return solution_view; };

    /** \brief Enable the "fix upgrade manually" mode on this resolver
     *  tab, using the given resolver manager.
     *
     *  This just adjusts the UI slightly to make it clear that the
     *  resolver is specifically focused on finding a solution for an
     *  upgrade problem and to provide feedback when the upgrade
     *  resolver's initial state is being recalculated for some reason
     *  (e.g., a cache reload).  It also prevents the resolver from
     *  being automatically destroyed when there are no more broken
     *  dependencies.
     *
     *  \param manager The manager to use in this tab, or \b NULL to
     *  disable the UI and show the "recalculating upgrade" progress
     *  bar.  The caller is responsible for invoking
     *  set_is_fix_upgrade_resolver() again, should "manager" ever
     *  become invalid, with a valid resolver manager object.
     */
    void set_fix_upgrade_resolver(resolver_manager *manager);

    /** \brief Invoked by the dashboard tab to pulse the progress bar
     *	in this tab.
     */
    void pulse_fix_upgrade_resolver_progress();
  };

  /** \brief Set up the global resolver tracker.
   *
   *  This is responsible for connecting up the signals that cause the
   *  resolver to be automatically triggered when there are broken
   *  packages, and for making sure that those signals are properly
   *  destroyed and recreated when the cache is closed and reopened.
   *  It should be called exactly once from main().
   *
   *  This also ensures that resman->state_changed() is triggered in
   *  the main thread whenever new resolver solutions are available.
   */
  void init_resolver();
}

#endif /* RESOLVER_H_ */