~ubuntu-branches/ubuntu/maverick/aptitude/maverick

« back to all changes in this revision

Viewing changes to .pc/11_ubuntu_uses_sudo/src/ui.cc

  • Committer: Steve Langasek
  • Date: 2010-07-08 00:11:10 UTC
  • mfrom: (1.1.17 sid)
  • Revision ID: steve.langasek@canonical.com-20100708001110-ycbfg1j1wc95ucxe
mergeĀ versionĀ 0.6.2.1-2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// ui.cc
 
2
//
 
3
//   Copyright 2000-2009 Daniel Burrows <dburrows@debian.org>
 
4
//
 
5
//   This program is free software; you can redistribute it and/or
 
6
//   modify it under the terms of the GNU General Public License as
 
7
//   published by the Free Software Foundation; either version 2 of
 
8
//   the License, or (at your option) any later version.
 
9
//
 
10
//   This program is distributed in the hope that it will be useful,
 
11
//   but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
//   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
13
//   General Public License for more details.
 
14
//
 
15
//   You should have received a copy of the GNU General Public License
 
16
//   along with this program; see the file COPYING.  If not, write to
 
17
//   the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 
18
//   Boston, MA 02111-1307, USA.
 
19
//
 
20
//  Various UI glue-code and routines.
 
21
 
 
22
#include "ui.h"
 
23
 
 
24
#include <sigc++/adaptors/bind.h>
 
25
#include <sigc++/adaptors/retype_return.h>
 
26
#include <sigc++/functors/ptr_fun.h>
 
27
#include <sigc++/functors/mem_fun.h>
 
28
 
 
29
#include <boost/make_shared.hpp>
 
30
#include <boost/shared_ptr.hpp>
 
31
#include <boost/weak_ptr.hpp>
 
32
 
 
33
#include <apt-pkg/acquire.h>
 
34
#include <apt-pkg/clean.h>
 
35
#include <apt-pkg/configuration.h>
 
36
#include <apt-pkg/error.h>
 
37
#include <apt-pkg/packagemanager.h>
 
38
 
 
39
#include <errno.h>
 
40
#include <fcntl.h>
 
41
#include <sys/wait.h>
 
42
#include <sys/stat.h>
 
43
#include <sys/types.h>
 
44
 
 
45
#include <fstream>
 
46
#include <sstream>
 
47
#include <utility>
 
48
 
 
49
#ifdef HAVE_CONFIG_H
 
50
#include <config.h>
 
51
#endif
 
52
 
 
53
#include "aptitude.h"
 
54
 
 
55
#include "apt_config_widgets.h"
 
56
#include "apt_options.h"
 
57
#include "broken_indicator.h"
 
58
#include "defaults.h"
 
59
#include "load_config.h"
 
60
#include "load_grouppolicy.h"
 
61
#include "load_pkgview.h"
 
62
#include "solution_dialog.h"
 
63
#include "solution_fragment.h"
 
64
#include "solution_screen.h"
 
65
 
 
66
#include <cwidget/curses++.h>
 
67
#include <cwidget/dialogs.h>
 
68
#include <cwidget/fragment.h>
 
69
#include <cwidget/toplevel.h>
 
70
#include <cwidget/widgets/button.h>
 
71
#include <cwidget/widgets/center.h>
 
72
#include <cwidget/widgets/editline.h>
 
73
#include <cwidget/widgets/frame.h>
 
74
#include <cwidget/widgets/label.h>
 
75
#include <cwidget/widgets/menu.h>
 
76
#include <cwidget/widgets/menubar.h>
 
77
#include <cwidget/widgets/multiplex.h>
 
78
#include <cwidget/widgets/pager.h>
 
79
#include <cwidget/widgets/scrollbar.h>
 
80
#include <cwidget/widgets/stacked.h>
 
81
#include <cwidget/widgets/statuschoice.h>
 
82
#include <cwidget/widgets/table.h>
 
83
#include <cwidget/widgets/text_layout.h>
 
84
#include <cwidget/widgets/togglebutton.h>
 
85
#include <cwidget/widgets/transient.h>
 
86
#include <cwidget/widgets/tree.h>
 
87
 
 
88
#include <mine/cmine.h>
 
89
 
 
90
#include <generic/apt/apt.h>
 
91
#include <generic/apt/apt_undo_group.h>
 
92
#include <generic/apt/aptitude_resolver_universe.h>
 
93
#include <generic/apt/config_signal.h>
 
94
#include <generic/apt/download_install_manager.h>
 
95
#include <generic/apt/download_update_manager.h>
 
96
#include <generic/apt/download_signal_log.h>
 
97
#include <generic/apt/resolver_manager.h>
 
98
 
 
99
#include <generic/problemresolver/exceptions.h>
 
100
#include <generic/problemresolver/solution.h>
 
101
 
 
102
#include <generic/util/temp.h>
 
103
#include <generic/util/util.h>
 
104
 
 
105
#include "dep_item.h"
 
106
#include "download_list.h"
 
107
#include "menu_tree.h"
 
108
#include "pkg_columnizer.h"
 
109
#include "pkg_grouppolicy.h"
 
110
#include "pkg_info_screen.h"
 
111
#include "pkg_tree.h"
 
112
#include "pkg_ver_item.h"
 
113
#include "pkg_view.h"
 
114
#include "safe_slot_event.h"
 
115
#include "ui_download_manager.h"
 
116
#include "progress.h"
 
117
 
 
118
using namespace std;
 
119
namespace cw = cwidget;
 
120
namespace cwidget
 
121
{
 
122
  using namespace widgets;
 
123
}
 
124
 
 
125
typedef generic_solution<aptitude_universe> aptitude_solution;
 
126
 
 
127
static cw::menubar_ref main_menu;
 
128
static cw::menu_ref views_menu;
 
129
 
 
130
static cw::stacked_ref main_stacked;
 
131
 
 
132
// Hmm, is this table the best idea?..
 
133
static cw::table_ref main_table;
 
134
 
 
135
static cw::multiplex_ref main_multiplex;
 
136
static cw::multiplex_ref main_status_multiplex;
 
137
 
 
138
// I think it's better to only have a single preview screen at once.  (note to
 
139
// self: data-segment stuff initializes to 0 already..)
 
140
static pkg_tree_ref active_preview_tree;
 
141
static cw::widget_ref active_preview;
 
142
 
 
143
// True if a download or package-list update is proceeding.  This hopefully will
 
144
// avoid the nasty possibility of collisions between them.
 
145
// FIXME: uses implicit locking -- if multithreading happens, should use a mutex
 
146
//       instead.
 
147
static bool active_download;
 
148
 
 
149
// While a status-widget download progress thingy is active, this will be
 
150
// set to it.
 
151
cw::widget_ref active_status_download;
 
152
 
 
153
sigc::signal0<void> file_quit;
 
154
 
 
155
sigc::signal0<bool, cw::util::accumulate_or> undo_undo;
 
156
sigc::signal0<bool, cw::util::accumulate_or> undo_undo_enabled;
 
157
 
 
158
sigc::signal0<void> package_states_changed;
 
159
 
 
160
sigc::signal0<bool, cw::util::accumulate_or> package_menu_enabled;
 
161
sigc::signal0<bool, cw::util::accumulate_or> package_forbid_enabled;
 
162
sigc::signal0<bool, cw::util::accumulate_or> package_information_enabled;
 
163
sigc::signal0<bool, cw::util::accumulate_or> package_cycle_information_enabled;
 
164
sigc::signal0<bool, cw::util::accumulate_or> package_changelog_enabled;
 
165
 
 
166
sigc::signal0<bool, cw::util::accumulate_or> find_search_enabled;
 
167
sigc::signal0<bool, cw::util::accumulate_or> find_search_back_enabled;
 
168
sigc::signal0<bool, cw::util::accumulate_or> find_research_enabled;
 
169
sigc::signal0<bool, cw::util::accumulate_or> find_repeat_search_back_enabled;
 
170
sigc::signal0<bool, cw::util::accumulate_or> find_limit_enabled;
 
171
sigc::signal0<bool, cw::util::accumulate_or> find_cancel_limit_enabled;
 
172
sigc::signal0<bool, cw::util::accumulate_or> find_broken_enabled;
 
173
 
 
174
sigc::signal0<bool, cw::util::accumulate_or> package_install;
 
175
sigc::signal0<bool, cw::util::accumulate_or> package_remove;
 
176
sigc::signal0<bool, cw::util::accumulate_or> package_purge;
 
177
sigc::signal0<bool, cw::util::accumulate_or> package_hold;
 
178
sigc::signal0<bool, cw::util::accumulate_or> package_keep;
 
179
sigc::signal0<bool, cw::util::accumulate_or> package_mark_auto;
 
180
sigc::signal0<bool, cw::util::accumulate_or> package_unmark_auto;
 
181
sigc::signal0<bool, cw::util::accumulate_or> package_forbid;
 
182
sigc::signal0<bool, cw::util::accumulate_or> package_information;
 
183
sigc::signal0<bool, cw::util::accumulate_or> package_cycle_information;
 
184
sigc::signal0<bool, cw::util::accumulate_or> package_changelog;
 
185
 
 
186
sigc::signal0<bool, cw::util::accumulate_or> resolver_toggle_rejected;
 
187
sigc::signal0<bool, cw::util::accumulate_or> resolver_toggle_rejected_enabled;
 
188
sigc::signal0<bool, cw::util::accumulate_or> resolver_toggle_approved;
 
189
sigc::signal0<bool, cw::util::accumulate_or> resolver_toggle_approved_enabled;
 
190
sigc::signal0<bool, cw::util::accumulate_or> resolver_view_target;
 
191
sigc::signal0<bool, cw::util::accumulate_or> resolver_view_target_enabled;
 
192
 
 
193
sigc::signal0<bool, cw::util::accumulate_or> find_search;
 
194
sigc::signal0<bool, cw::util::accumulate_or> find_search_back;
 
195
sigc::signal0<bool, cw::util::accumulate_or> find_research;
 
196
sigc::signal0<bool, cw::util::accumulate_or> find_repeat_search_back;
 
197
sigc::signal0<bool, cw::util::accumulate_or> find_limit;
 
198
sigc::signal0<bool, cw::util::accumulate_or> find_cancel_limit;
 
199
sigc::signal0<bool, cw::util::accumulate_or> find_broken;
 
200
 
 
201
sigc::signal1<void, bool> install_finished;
 
202
sigc::signal1<void, bool> update_finished;
 
203
 
 
204
const char *default_pkgstatusdisplay="%d";
 
205
const char *default_pkgheaderdisplay="%N %n #%B %u %o";
 
206
const char *default_grpstr="task,status,section(subdirs,passthrough),section(topdir)";
 
207
const char *confirm_delete_essential_str=N_("Yes, I am aware this is a very bad idea");
 
208
 
 
209
 
 
210
void ui_start_download(bool hide_preview)
 
211
{
 
212
  active_download = true;
 
213
 
 
214
  if(apt_cache_file != NULL)
 
215
    (*apt_cache_file)->set_read_only(true);
 
216
 
 
217
  if(hide_preview && active_preview.valid())
 
218
    active_preview->destroy();
 
219
}
 
220
 
 
221
void ui_stop_download()
 
222
{
 
223
  active_download = false;
 
224
 
 
225
  if(apt_cache_file != NULL)
 
226
    (*apt_cache_file)->set_read_only(false);
 
227
}
 
228
 
 
229
static cw::fragment *apt_error_fragment()
 
230
{
 
231
  vector<cw::fragment *> frags;
 
232
 
 
233
  if(_error->empty())
 
234
    frags.push_back(cw::text_fragment(_("Er, there aren't any errors, this shouldn't have happened..")));
 
235
  else while(!_error->empty())
 
236
    {
 
237
      string currerr, tag;
 
238
      bool iserr=_error->PopMessage(currerr);
 
239
      if(iserr)
 
240
        tag=_("E:");
 
241
      else
 
242
        tag=_("W:");
 
243
 
 
244
      frags.push_back(indentbox(0, 3,
 
245
                                wrapbox(cw::fragf("%B%s%b %s",
 
246
                                              tag.c_str(),
 
247
                                              currerr.c_str()))));
 
248
    }
 
249
 
 
250
  return cw::sequence_fragment(frags);
 
251
}
 
252
 
 
253
// Handles "search" dialogs for pagers
 
254
static void pager_search(cw::pager &p)
 
255
{
 
256
  prompt_string(W_("Search for:"),
 
257
                p.get_last_search(),
 
258
                cw::util::arg(sigc::mem_fun(p, &cw::pager::search_for)),
 
259
                NULL,
 
260
                NULL,
 
261
                NULL);
 
262
}
 
263
 
 
264
// similar
 
265
static void pager_repeat_search(cw::pager &p)
 
266
{
 
267
  p.search_for(L"");
 
268
}
 
269
 
 
270
static void pager_repeat_search_back(cw::pager &p)
 
271
{
 
272
  p.search_back_for(L"");
 
273
}
 
274
 
 
275
static cw::widget_ref make_error_dialog(const cw::text_layout_ref &layout)
 
276
{
 
277
  cw::table_ref t=cw::table::create();
 
278
 
 
279
  cw::scrollbar_ref s=cw::scrollbar::create(cw::scrollbar::VERTICAL);
 
280
 
 
281
  t->add_widget(layout, 0, 0, 1, 1, true, true);
 
282
  t->add_widget_opts(s, 0, 1, 1, 1,
 
283
                     cw::table::ALIGN_RIGHT,
 
284
                     cw::table::ALIGN_CENTER | cw::table::FILL);
 
285
 
 
286
  layout->location_changed.connect(sigc::mem_fun(s.unsafe_get_ref(), &cw::scrollbar::set_slider));
 
287
  s->scrollbar_interaction.connect(sigc::mem_fun(layout.unsafe_get_ref(), &cw::text_layout::scroll));
 
288
 
 
289
  return cw::dialogs::ok(t, NULL, W_("Ok"), cw::get_style("Error"));
 
290
}
 
291
 
 
292
// blah, I hate C++
 
293
static void do_null_ptr(cw::text_layout_ref *p)
 
294
{
 
295
  *p=NULL;
 
296
}
 
297
 
 
298
void check_apt_errors()
 
299
{
 
300
  if(_error->empty())
 
301
    return;
 
302
 
 
303
  static cw::text_layout_ref error_dialog_layout = NULL;
 
304
 
 
305
  if(!error_dialog_layout.valid())
 
306
    {
 
307
      error_dialog_layout = cw::text_layout::create(apt_error_fragment());
 
308
      error_dialog_layout->destroyed.connect(sigc::bind(sigc::ptr_fun(do_null_ptr), &error_dialog_layout));
 
309
 
 
310
      main_stacked->add_visible_widget(make_error_dialog(error_dialog_layout),
 
311
                                       true);
 
312
    }
 
313
  else
 
314
    error_dialog_layout->append_fragment(apt_error_fragment());
 
315
}
 
316
 
 
317
static void read_only_permissions_table_destroyed(apt_config_widget &w)
 
318
{
 
319
  w.commit();
 
320
  apt_dumpcfg(PACKAGE);
 
321
 
 
322
  delete &w;
 
323
}
 
324
 
 
325
static bool do_read_only_permission()
 
326
{
 
327
  if(active_download)
 
328
    return false;
 
329
  else
 
330
    {
 
331
      (*apt_cache_file)->set_read_only(false);
 
332
 
 
333
 
 
334
      if(!aptcfg->FindB(PACKAGE "::Suppress-Read-Only-Warning", false))
 
335
        {
 
336
          cw::table_ref t(cw::table::create());
 
337
 
 
338
          cw::fragment *f = wrapbox(cw::text_fragment(_("WARNING: the package cache is opened in read-only mode!  This change and all subsequent changes will not be saved unless you stop all other running apt-based programs and select \"Become root\" from the Actions menu.")));
 
339
 
 
340
          t->add_widget_opts(cw::text_layout::create(f),
 
341
                             0, 0, 1, 1, cw::table::EXPAND | cw::table::FILL | cw::table::SHRINK,
 
342
                             cw::table::EXPAND | cw::table::FILL);
 
343
 
 
344
          apt_bool_widget *w = new apt_bool_widget(_("Never display this message again."),
 
345
                                                   PACKAGE "::Suppress-Read-Only-Warning", false);
 
346
 
 
347
          // HACK:
 
348
          t->add_widget_opts(cw::label::create(""), 1, 0, 1, 1,
 
349
                             cw::table::EXPAND | cw::table::FILL | cw::table::SHRINK,
 
350
                             0);
 
351
 
 
352
          t->add_widget_opts(w->cb, 2, 0, 1, 1,
 
353
                             cw::table::EXPAND | cw::table::FILL | cw::table::SHRINK,
 
354
                             cw::table::EXPAND | cw::table::FILL);
 
355
 
 
356
          // HACK:
 
357
          t->add_widget_opts(cw::label::create(""), 3, 0, 1, 1,
 
358
                             cw::table::EXPAND | cw::table::FILL | cw::table::SHRINK,
 
359
                             0);
 
360
 
 
361
          cw::button_ref ok(cw::button::create(_("Ok")));
 
362
 
 
363
          t->add_widget_opts(ok, 4, 0, 1, 1,
 
364
                             cw::table::EXPAND | cw::table::SHRINK,
 
365
                             cw::table::EXPAND);
 
366
 
 
367
          t->show_all();
 
368
 
 
369
          t->focus_widget(ok);
 
370
 
 
371
          cw::frame_ref frame = cw::frame::create(t);
 
372
          frame->set_bg_style(cw::style_attrs_flip(A_REVERSE));
 
373
 
 
374
          cw::center_ref c = cw::center::create(frame);
 
375
 
 
376
          ok->pressed.connect(sigc::mem_fun(c.unsafe_get_ref(), &cw::widget::destroy));
 
377
          c->destroyed.connect(sigc::bind(sigc::ptr_fun(&read_only_permissions_table_destroyed), sigc::ref(*w)));
 
378
 
 
379
          popup_widget(c);
 
380
        }
 
381
 
 
382
      return true;
 
383
    }
 
384
}
 
385
 
 
386
static void do_read_only_fail()
 
387
{
 
388
  eassert(active_download);
 
389
 
 
390
  show_message(_("You may not modify the state of any package while a download is underway."));
 
391
}
 
392
 
 
393
static void do_connect_read_only_callbacks()
 
394
{
 
395
  if(apt_cache_file != NULL)
 
396
    {
 
397
      (*apt_cache_file)->read_only_permission.connect(sigc::ptr_fun(&do_read_only_permission));
 
398
      (*apt_cache_file)->read_only_fail.connect(sigc::ptr_fun(&do_read_only_fail));
 
399
    }
 
400
}
 
401
 
 
402
// Runs a sub-aptitude with the same selections that the user
 
403
// has currently made, but as root.
 
404
//
 
405
// Because tagfiles don't work on FIFOs, and su closes all open fds,
 
406
// this is a lot hairier than it should be.
 
407
//
 
408
// This writes the current status to a file in a designated temporary
 
409
// directory, then loads it in a su'd instance.  A FIFO is used to
 
410
// make the reader block until the writer is done writing.  Not
 
411
// foolproof but the user would have to go to a lot of pointless
 
412
// trouble to screw it up.
 
413
//
 
414
// Note that the deletion of the temporary files is safe, since this
 
415
// routine blocks until the sub-process exits.
 
416
 
 
417
static void do_su_to_root(string args)
 
418
{
 
419
  if(getuid()==0)
 
420
    {
 
421
      show_message(_("You already are root!"));
 
422
      return;
 
423
    }
 
424
 
 
425
  std::string root_command = aptcfg->Find(PACKAGE "::Get-Root-Command",
 
426
                                          "su:/bin/su");
 
427
 
 
428
  if(root_command == "su")
 
429
    root_command = "su:/bin/su";
 
430
  else if(root_command == "sudo")
 
431
    root_command = "sudo:/usr/bin/sudo";
 
432
 
 
433
  std::string::size_type splitloc = root_command.find(':');
 
434
  if(splitloc == std::string::npos)
 
435
    {
 
436
      _error->Error(_("Invalid Get-Root-Command; it should start with su: or sudo:"));
 
437
      return;
 
438
    }
 
439
 
 
440
  std::string protocol(root_command, 0, splitloc);
 
441
  if(protocol != "su" && protocol != "sudo")
 
442
    {
 
443
      _error->Error(_("Invalid Get-Root-Command; it should start with su: or sudo:, not %s:"), protocol.c_str());
 
444
      return;
 
445
    }
 
446
  std::string root_program(root_command, splitloc + 1);
 
447
 
 
448
  temp::name statusname("pkgstates");
 
449
  temp::name fifoname("control");
 
450
 
 
451
  if(mkfifo(fifoname.get_name().c_str(), 0600) != 0)
 
452
    {
 
453
      _error->Errno("do_su_to_root",
 
454
                    "Couldn't create temporary FIFO");
 
455
      return;
 
456
    }
 
457
 
 
458
  // Shut curses down.  This is done before we fork because otherwise
 
459
  // we'll end up with curses apparently being initialized in the
 
460
  // child and with mutexes locked (despite the fact that no thread
 
461
  // holds them), and get horribly confused, especially if we try to
 
462
  // invoke exit(3).
 
463
  //
 
464
  // An alternative is to invoke _exit(2) instead, but it seems
 
465
  // cleaner to suspend curses right here.
 
466
  cw::toplevel::suspend();
 
467
 
 
468
  int pid=fork();
 
469
 
 
470
  if(pid==-1)
 
471
    {
 
472
      _error->Error(_("Unable to fork: %s"), strerror(errno));
 
473
      cw::toplevel::resume();
 
474
    }
 
475
  else if(pid==0) // I'm a child!
 
476
    {
 
477
      // Read one byte from the FIFO for synchronization
 
478
      char tmp;
 
479
      int fd = open(fifoname.get_name().c_str(), O_RDONLY);
 
480
      read(fd, &tmp, 1); // Will block until the other process writes.
 
481
      close(fd);
 
482
 
 
483
      // It's ok to use argv0 to generate the command,
 
484
      // since the user has to authenticate themselves as root (ie, if
 
485
      // they're going to do something evil that way they'd have su'd
 
486
      // directly)
 
487
      //
 
488
      // What happens if tmpdir has spaces in it?  Can I get more
 
489
      // control over how the su-to-root function is called?
 
490
      if(protocol == "su")
 
491
        {
 
492
          std::ostringstream cmdbuf;
 
493
          cmdbuf << argv0 << " --no-gui -S "
 
494
                 << statusname.get_name() << " "
 
495
                 << args;
 
496
          execl(root_program.c_str(), root_program.c_str(), "-c", cmdbuf.str().c_str(), NULL);
 
497
 
 
498
          exit(1);
 
499
        }
 
500
      else if(protocol == "sudo")
 
501
        {
 
502
          std::vector<std::string> cmdlist;
 
503
          // Split whitespace in the input command.
 
504
          std::string command = root_program + " " + argv0 + " --no-gui -S " + statusname.get_name() + " " + args;
 
505
          std::string::const_iterator it = command.begin();
 
506
          while(it != command.end() && isspace(*it))
 
507
            ++it;
 
508
 
 
509
          while(it != command.end())
 
510
            {
 
511
              std::string tmp;
 
512
              while(it != command.end() && !isspace(*it))
 
513
                {
 
514
                  tmp += *it;
 
515
                  ++it;
 
516
                }
 
517
 
 
518
              cmdlist.push_back(tmp);
 
519
 
 
520
              while(it != command.end() && isspace(*it))
 
521
                ++it;
 
522
            }
 
523
 
 
524
          const char **real_cmd = new const char*[cmdlist.size() + 1];
 
525
          for(std::string::size_type i = 0; i < cmdlist.size(); ++i)
 
526
            real_cmd[i] = cmdlist[i].c_str();
 
527
          real_cmd[cmdlist.size()] = NULL;
 
528
 
 
529
          execv(real_cmd[0], const_cast<char* const*>(real_cmd));
 
530
 
 
531
          std::string errmsg = ssprintf("aptitude: do_su_to_root: exec \"%s\" failed", root_program.c_str());
 
532
          perror(errmsg.c_str());
 
533
 
 
534
          delete[] real_cmd;
 
535
 
 
536
          // Eek, something bad happened.
 
537
          exit(1);
 
538
        }
 
539
      else
 
540
        exit(1); // Should never happen -- see the test above.
 
541
    }
 
542
  else
 
543
    {
 
544
      int status;
 
545
      OpProgress foo; // Need a generic non-outputting progress bar
 
546
 
 
547
      // Save the selection list.  Check first if it's NULL to handle the
 
548
      // case of a closed cache.
 
549
      if(apt_cache_file != NULL)
 
550
        (*apt_cache_file)->save_selection_list(foo, statusname.get_name().c_str());
 
551
 
 
552
      // Ok, wake the other process up.
 
553
      char tmp=0;
 
554
      int fd=open(fifoname.get_name().c_str(), O_WRONLY);
 
555
      write(fd, &tmp, 1);
 
556
      close(fd);
 
557
 
 
558
      // Wait for a while so we don't accidentally daemonize ourselves.
 
559
      while(waitpid(pid, &status, 0)!=pid)
 
560
        ;
 
561
 
 
562
      if(!WIFEXITED(status) || WEXITSTATUS(status))
 
563
        {
 
564
          _error->Error("%s",
 
565
                        _("Subprocess exited with an error -- did you type your password correctly?"));
 
566
          cw::toplevel::resume();
 
567
          check_apt_errors();
 
568
          // We have to clear these out or the cache won't reload properly (?)
 
569
 
 
570
          progress_ref p = gen_progress_bar();
 
571
          apt_reload_cache(p->get_progress().unsafe_get_ref(), true, statusname.get_name().c_str());
 
572
          p->destroy();
 
573
        }
 
574
      else
 
575
        {
 
576
          // Clear out our references to these objects so they get
 
577
          // removed, although the shutdown routine should also do
 
578
          // that.
 
579
          statusname = temp::name();
 
580
          fifoname   = temp::name();
 
581
 
 
582
          exit(0);
 
583
        }
 
584
    }
 
585
}
 
586
 
 
587
static bool su_to_root_enabled()
 
588
{
 
589
  return getuid()!=0;
 
590
}
 
591
 
 
592
static void update_menubar_autohide()
 
593
{
 
594
  main_menu->set_always_visible(main_multiplex->num_children()==0 ||
 
595
                                !aptcfg->FindB(PACKAGE "::UI::Menubar-Autohide",
 
596
                                               false));
 
597
}
 
598
 
 
599
cw::widget_ref reload_message;
 
600
static void do_show_reload_message()
 
601
{
 
602
  if(!reload_message.valid())
 
603
    {
 
604
      cw::widget_ref w = cw::frame::create(cw::label::create(_("Loading cache")));
 
605
      reload_message  = cw::center::create(w);
 
606
      reload_message->show_all();
 
607
      popup_widget(reload_message);
 
608
 
 
609
      cw::toplevel::tryupdate();
 
610
    }
 
611
}
 
612
 
 
613
static void do_hide_reload_message()
 
614
{
 
615
  if(reload_message.valid())
 
616
    {
 
617
      reload_message->destroy();
 
618
      reload_message = NULL;
 
619
      cw::toplevel::tryupdate();
 
620
    }
 
621
}
 
622
 
 
623
/** \brief If this is \b true, there's a "really quit aptitude?"
 
624
 *  prompt displayed.
 
625
 *
 
626
 *  The sole purpose of this variable is to prevent the dialog
 
627
 *  box that asks about quitting from showing up multiple times.
 
628
 */
 
629
static bool really_quit_active = false;
 
630
 
 
631
static void do_really_quit_answer(bool should_i_quit)
 
632
{
 
633
  really_quit_active = false;
 
634
 
 
635
  if(should_i_quit)
 
636
    file_quit();
 
637
}
 
638
 
 
639
static void do_quit()
 
640
{
 
641
  if(aptcfg->FindB(PACKAGE "::UI::Prompt-On-Exit", true))
 
642
    {
 
643
      if(!really_quit_active)
 
644
        {
 
645
          really_quit_active = true;
 
646
          prompt_yesno(_("Really quit Aptitude?"), false,
 
647
                       cw::util::arg(sigc::bind(ptr_fun(do_really_quit_answer), true)),
 
648
                       cw::util::arg(sigc::bind(ptr_fun(do_really_quit_answer), false)));
 
649
        }
 
650
    }
 
651
  else
 
652
    file_quit();
 
653
}
 
654
 
 
655
static void do_destroy_visible()
 
656
{
 
657
  if(aptcfg->FindB(PACKAGE "::UI::Exit-On-Last-Close", true) &&
 
658
     main_multiplex->num_children()<=1)
 
659
    do_quit();
 
660
  else
 
661
    {
 
662
      cw::widget_ref w=main_multiplex->visible_widget();
 
663
      if(w.valid())
 
664
        w->destroy();
 
665
 
 
666
      // If all the screens are destroyed, we make the menu visible (so the
 
667
      // user knows that something is still alive :) )
 
668
      update_menubar_autohide();
 
669
    }
 
670
}
 
671
 
 
672
static bool view_next_prev_enabled()
 
673
{
 
674
  return main_multiplex->num_visible()>1;
 
675
}
 
676
 
 
677
static bool any_view_visible()
 
678
{
 
679
  return main_multiplex->visible_widget().valid();
 
680
}
 
681
 
 
682
// These are necessary because main_multiplex isn't created until after
 
683
// the slot in the static initializer..
 
684
// (creating the menu info at a later point would solve this problem)
 
685
static void do_view_next()
 
686
{
 
687
  main_multiplex->cycle_forward();
 
688
}
 
689
 
 
690
static void do_view_prev()
 
691
{
 
692
  main_multiplex->cycle_backward();
 
693
}
 
694
 
 
695
static void do_show_options_tree()
 
696
{
 
697
  cw::widget_ref w = aptitude::ui::config::make_options_tree();
 
698
  add_main_widget(w,
 
699
                  _("Preferences"),
 
700
                  _("Change the behavior of aptitude"),
 
701
                  _("Preferences"));
 
702
  w->show_all();
 
703
}
 
704
 
 
705
#if 0
 
706
static void do_show_ui_options_dlg()
 
707
{
 
708
  cw::widget_ref w = make_ui_options_dialog();
 
709
  main_stacked->add_visible_widget(w, true);
 
710
  w->show();
 
711
}
 
712
 
 
713
static void do_show_misc_options_dlg()
 
714
{
 
715
  cw::widget_ref w=make_misc_options_dialog();
 
716
  main_stacked->add_visible_widget(w, true);
 
717
  w->show();
 
718
}
 
719
 
 
720
static void do_show_dependency_options_dlg()
 
721
{
 
722
  cw::widget_ref w=make_dependency_options_dialog();
 
723
  main_stacked->add_visible_widget(w, true);
 
724
  w->show();
 
725
}
 
726
#endif
 
727
 
 
728
static void really_do_revert_options()
 
729
{
 
730
  apt_revertoptions();
 
731
  apt_dumpcfg(PACKAGE);
 
732
}
 
733
 
 
734
static void do_revert_options()
 
735
{
 
736
  prompt_yesno(_("Really discard your personal settings and reload the defaults?"),
 
737
               false,
 
738
               cw::util::arg(sigc::ptr_fun(really_do_revert_options)),
 
739
               NULL);
 
740
}
 
741
 
 
742
static cw::widget_ref make_default_view(const menu_tree_ref &mainwidget,
 
743
                                       pkg_signal *sig,
 
744
                                       desc_signal *desc_sig,
 
745
                                       bool allow_visible_desc=true,
 
746
                                       bool show_reason_first=false)
 
747
{
 
748
  if(aptcfg->Exists(PACKAGE "::UI::Default-Package-View"))
 
749
    {
 
750
      list<package_view_item> *format=load_pkgview(PACKAGE "::UI::Default-Package-View");
 
751
 
 
752
      if(format)
 
753
        {
 
754
          // The unsafe_get_ref is to convert mainwidget to be a
 
755
          // menu_redirect pointer.
 
756
          cw::widget_ref rval=make_package_view(*format, mainwidget,
 
757
                                               mainwidget.unsafe_get_ref(), sig,
 
758
                                               desc_sig, show_reason_first);
 
759
          delete format;
 
760
 
 
761
          if(rval.valid())
 
762
            return rval;
 
763
        }
 
764
    }
 
765
 
 
766
  list<package_view_item> basic_format;
 
767
 
 
768
  // FIXME: do the config lookup inside the package-view code?
 
769
  basic_format.push_back(package_view_item("static1",
 
770
                                           parse_columns(cw::util::transcode(aptcfg->Find(PACKAGE "::UI::Package-Header-Format", default_pkgheaderdisplay)),
 
771
                                                         pkg_item::pkg_columnizer::parse_column_type,
 
772
                                                         pkg_item::pkg_columnizer::defaults),
 
773
                                           PACKAGE "::UI::Package-Header-Format",
 
774
                                           0, 0, 1, 1,
 
775
                                           cw::table::ALIGN_CENTER | cw::table::EXPAND | cw::table::FILL | cw::table::SHRINK,
 
776
                                           cw::table::ALIGN_CENTER,
 
777
                                           cw::get_style("Header"),
 
778
                                           "",
 
779
                                           "",
 
780
                                           true));
 
781
 
 
782
  basic_format.push_back(package_view_item("main", 1, 0, 1, 1,
 
783
                                           cw::table::EXPAND | cw::table::FILL | cw::table::SHRINK,
 
784
                                           cw::table::EXPAND | cw::table::FILL | cw::table::SHRINK,
 
785
                                           cw::style(),
 
786
                                           true));
 
787
 
 
788
  basic_format.push_back(package_view_item("static2",
 
789
                                           parse_columns(cw::util::transcode(aptcfg->Find(PACKAGE "::UI::Package-Status-Format", default_pkgstatusdisplay)),
 
790
                                                         pkg_item::pkg_columnizer::parse_column_type,
 
791
                                                         pkg_item::pkg_columnizer::defaults),
 
792
                                           PACKAGE "::UI::Package-Status-Format",
 
793
                                           2, 0, 1, 1,
 
794
                                           cw::table::ALIGN_CENTER | cw::table::EXPAND | cw::table::FILL | cw::table::SHRINK,
 
795
                                           cw::table::ALIGN_CENTER,
 
796
                                           cw::get_style("Status"),
 
797
                                           "", "",
 
798
                                           true));
 
799
 
 
800
  basic_format.push_back(package_view_item("desc", 3, 0, 1, 1,
 
801
                                           cw::table::EXPAND | cw::table::FILL | cw::table::SHRINK,
 
802
                                           cw::table::EXPAND | cw::table::FILL | cw::table::SHRINK,
 
803
                                           cw::style(),
 
804
                                           "ShowHideDescription", "",
 
805
                                           allow_visible_desc && aptcfg->FindB(PACKAGE "::UI::Description-Visible-By-Default", true)));
 
806
 
 
807
  return make_package_view(basic_format, mainwidget,
 
808
                           mainwidget.unsafe_get_ref(),
 
809
                           sig, desc_sig,
 
810
                           show_reason_first);
 
811
}
 
812
 
 
813
void do_new_package_view(OpProgress &progress)
 
814
{
 
815
  pkg_grouppolicy_factory *grp=NULL;
 
816
  std::string grpstr="";
 
817
 
 
818
  if(aptcfg->Exists(PACKAGE "::UI::Default-Grouping"))
 
819
    {
 
820
      grpstr=aptcfg->Find(PACKAGE "::UI::Default-Grouping");
 
821
      grp=parse_grouppolicy(grpstr);
 
822
    }
 
823
 
 
824
  if(!grp) // Fallback
 
825
    {
 
826
      grpstr=default_grpstr;
 
827
      grp=parse_grouppolicy(grpstr);
 
828
 
 
829
      if(!grp)
 
830
        // Eek!  The default grouping failed to parse.  Fall all the
 
831
        // way back.
 
832
        grp=new pkg_grouppolicy_task_factory(new pkg_grouppolicy_status_factory(new pkg_grouppolicy_section_factory(pkg_grouppolicy_section_factory::split_subdirs,true,new pkg_grouppolicy_section_factory(pkg_grouppolicy_section_factory::split_topdir,false,new pkg_grouppolicy_end_factory()))));
 
833
    }
 
834
 
 
835
  pkg_tree_ref tree=pkg_tree::create(grpstr.c_str(), grp);
 
836
 
 
837
  add_main_widget(make_default_view(tree,
 
838
                                    &tree->selected_signal,
 
839
                                    &tree->selected_desc_signal),
 
840
                  _("Packages"),
 
841
                  _("View available packages and choose actions to perform"),
 
842
                  _("Packages"));
 
843
 
 
844
  tree->build_tree(progress);
 
845
}
 
846
 
 
847
// For signal connections.
 
848
static void do_new_package_view_with_new_bar()
 
849
{
 
850
  progress_ref p = gen_progress_bar();
 
851
  do_new_package_view(*p->get_progress().unsafe_get_ref());
 
852
  p->destroy();
 
853
}
 
854
 
 
855
static void do_new_recommendations_view()
 
856
{
 
857
  progress_ref p = gen_progress_bar();
 
858
 
 
859
  pkg_grouppolicy_factory *grp = new pkg_grouppolicy_end_factory();
 
860
  std::string grpstr="section(subdirs, passthrough)";
 
861
 
 
862
  pkg_tree_ref tree=pkg_tree::create(grpstr.c_str(), grp,
 
863
                                     L"!~v!~i~RBrecommends:~i");
 
864
 
 
865
  add_main_widget(make_default_view(tree,
 
866
                                    &tree->selected_signal,
 
867
                                    &tree->selected_desc_signal,
 
868
                                    true,
 
869
                                    true),
 
870
                  _("Recommended Packages"),
 
871
                  _("View packages that it is recommended that you install"),
 
872
                  _("Recommendations"));
 
873
 
 
874
  tree->build_tree(*p->get_progress().unsafe_get_ref());
 
875
  p->destroy();
 
876
}
 
877
 
 
878
void do_new_flat_view(OpProgress &progress)
 
879
{
 
880
  pkg_grouppolicy_factory *grp = new pkg_grouppolicy_end_factory;
 
881
  pkg_tree_ref tree = pkg_tree::create("", grp);
 
882
  tree->set_limit(cw::util::transcode("!~v"));
 
883
 
 
884
  add_main_widget(make_default_view(tree,
 
885
                                    &tree->selected_signal,
 
886
                                    &tree->selected_desc_signal),
 
887
                  _("Packages"),
 
888
                  _("View available packages and choose actions to perform"),
 
889
                  _("Packages"));
 
890
 
 
891
  tree->build_tree(progress);
 
892
}
 
893
 
 
894
// For signal connections.
 
895
static void do_new_flat_view_with_new_bar()
 
896
{
 
897
  progress_ref p = gen_progress_bar();
 
898
  do_new_flat_view(*p->get_progress().unsafe_get_ref());
 
899
  p->destroy();
 
900
}
 
901
 
 
902
static void do_new_tag_view_with_new_bar()
 
903
{
 
904
  progress_ref p = gen_progress_bar();
 
905
 
 
906
  pkg_grouppolicy_factory *grp = NULL;
 
907
  string grpstr = "tag";
 
908
  grp = parse_grouppolicy(grpstr);
 
909
 
 
910
  pkg_tree_ref tree = pkg_tree::create(grpstr.c_str(), grp);
 
911
 
 
912
  add_main_widget(make_default_view(tree,
 
913
                                    &tree->selected_signal,
 
914
                                    &tree->selected_desc_signal),
 
915
                  _("Packages"),
 
916
                  _("View available packages and choose actions to perform"),
 
917
                  _("Packages"));
 
918
 
 
919
  tree->build_tree(*p->get_progress().unsafe_get_ref());
 
920
  p->destroy();
 
921
}
 
922
 
 
923
void do_new_hier_view(OpProgress &progress)
 
924
{
 
925
  pkg_grouppolicy_factory *grp=NULL;
 
926
  std::string grpstr="";
 
927
 
 
928
  grpstr="hier";
 
929
  grp=parse_grouppolicy(grpstr);
 
930
 
 
931
  pkg_tree_ref tree=pkg_tree::create(grpstr.c_str(), grp);
 
932
  tree->set_limit(cw::util::transcode("!~v"));
 
933
  //tree->set_hierarchical(false);
 
934
 
 
935
  add_main_widget(make_default_view(tree,
 
936
                                    &tree->selected_signal,
 
937
                                    &tree->selected_desc_signal),
 
938
                  _("Packages"),
 
939
                  _("View available packages and choose actions to perform"),
 
940
                  _("Packages"));
 
941
  tree->build_tree(progress);
 
942
}
 
943
 
 
944
// For signal connections.
 
945
static void do_new_hier_view_with_new_bar()
 
946
{
 
947
  progress_ref p=gen_progress_bar();
 
948
  do_new_hier_view(*p->get_progress().unsafe_get_ref());
 
949
  p->destroy();
 
950
}
 
951
 
 
952
cw::widget_ref make_info_screen(const pkgCache::PkgIterator &pkg,
 
953
                               const pkgCache::VerIterator &ver)
 
954
{
 
955
  pkg_info_screen_ref w = pkg_info_screen::create(pkg, ver);
 
956
  cw::widget_ref rval    = make_default_view(w, w->get_sig(), w->get_desc_sig(), false);
 
957
  w->repeat_signal(); // force the status line in the view to update
 
958
  return rval;
 
959
}
 
960
 
 
961
cw::widget_ref make_dep_screen(const pkgCache::PkgIterator &pkg,
 
962
                              const pkgCache::VerIterator &ver,
 
963
                              bool reverse)
 
964
{
 
965
  pkg_dep_screen_ref w = pkg_dep_screen::create(pkg, ver, reverse);
 
966
  cw::widget_ref rval   = make_default_view(w, w->get_sig(), w->get_desc_sig(), true);
 
967
  w->repeat_signal(); // force the status line in the view to update
 
968
  return rval;
 
969
}
 
970
 
 
971
cw::widget_ref make_ver_screen(const pkgCache::PkgIterator &pkg)
 
972
{
 
973
  pkg_ver_screen_ref w = pkg_ver_screen::create(pkg);
 
974
  cw::widget_ref rval   = make_default_view(w, w->get_sig(), w->get_desc_sig(), true);
 
975
  w->repeat_signal();
 
976
  return rval;
 
977
}
 
978
 
 
979
static void do_help_about()
 
980
{
 
981
  cw::fragment *f = cw::fragf(_("Aptitude %s%n%nCopyright 2000-2008 Daniel Burrows.%n"
 
982
                                "%n"
 
983
                                "aptitude comes with %BABSOLUTELY NO WARRANTY%b; for details see 'license' in the Help menu.  This is free software, and you are welcome to redistribute it under certain conditions; see 'license' for details."), VERSION);
 
984
 
 
985
  cw::widget_ref w=cw::dialogs::ok(wrapbox(f));
 
986
  w->show_all();
 
987
 
 
988
  popup_widget(w);
 
989
}
 
990
 
 
991
/** Set up a new top-level file-viewing widget with a scrollbar. */
 
992
static cw::widget_ref setup_fileview(const std::string &filename,
 
993
                                    const char *encoding,
 
994
                                    const std::string &menudesc,
 
995
                                    const std::string &longmenudesc,
 
996
                                    const std::string &tabdesc)
 
997
{
 
998
 
 
999
  cw::table_ref t      = cw::table::create();
 
1000
  cw::scrollbar_ref s  = cw::scrollbar::create(cw::scrollbar::VERTICAL);
 
1001
  cw::file_pager_ref p = cw::file_pager::create(filename, encoding);
 
1002
 
 
1003
  p->line_changed.connect(sigc::mem_fun(s.unsafe_get_ref(), &cw::scrollbar::set_slider));
 
1004
  s->scrollbar_interaction.connect(sigc::mem_fun(p.unsafe_get_ref(), &cw::pager::scroll_page));
 
1005
  p->scroll_top(); // Force a scrollbar update.
 
1006
 
 
1007
  p->connect_key("Search", &cw::config::global_bindings,
 
1008
                 sigc::bind(sigc::ptr_fun(&pager_search), p.weak_ref()));
 
1009
  p->connect_key("ReSearch", &cw::config::global_bindings,
 
1010
                 sigc::bind(sigc::ptr_fun(&pager_repeat_search), p.weak_ref()));
 
1011
  p->connect_key("RepeatSearchBack", &cw::config::global_bindings,
 
1012
                 sigc::bind(sigc::ptr_fun(&pager_repeat_search_back), p.weak_ref()));
 
1013
 
 
1014
  t->add_widget_opts(p, 0, 0, 1, 1,
 
1015
                     cw::table::EXPAND | cw::table::FILL | cw::table::SHRINK,
 
1016
                     cw::table::EXPAND | cw::table::FILL);
 
1017
  t->add_widget_opts(s, 0, 1, 1, 1,
 
1018
                     0, cw::table::EXPAND | cw::table::FILL);
 
1019
 
 
1020
  s->show();
 
1021
  p->show();
 
1022
 
 
1023
  add_main_widget(t, menudesc, longmenudesc, tabdesc);
 
1024
  return t;
 
1025
}
 
1026
 
 
1027
static void do_help_license()
 
1028
{
 
1029
  setup_fileview(HELPDIR "/COPYING",
 
1030
                 NULL,
 
1031
                 _("License"),
 
1032
                 _("View the terms under which you may copy and distribute aptitude"),
 
1033
                 _("License"));
 
1034
}
 
1035
 
 
1036
static void do_help_help()
 
1037
{
 
1038
  static cw::widget_ref fileview_widget;
 
1039
 
 
1040
  if(fileview_widget.valid())
 
1041
    {
 
1042
      fileview_widget->show();
 
1043
      return;
 
1044
    }
 
1045
 
 
1046
  std::string filename = ssprintf(HELPDIR "/%s", _("help.txt"));
 
1047
 
 
1048
  const char *encoding = P_("Encoding of help.txt|UTF-8");
 
1049
 
 
1050
  // Deal with missing localized docs.
 
1051
  if(access(filename.c_str(), R_OK) != 0)
 
1052
    {
 
1053
      filename = HELPDIR "/help.txt";
 
1054
      encoding = "UTF-8";
 
1055
    }
 
1056
 
 
1057
  fileview_widget = setup_fileview(filename,
 
1058
                                   encoding,
 
1059
                                   _("Online Help"),
 
1060
                                   _("View a brief introduction to aptitude"),
 
1061
                                   _("Help"));
 
1062
  fileview_widget->destroyed.connect(sigc::mem_fun(fileview_widget,
 
1063
                                                   &cw::widget_ref::clear));
 
1064
}
 
1065
 
 
1066
static void do_help_readme()
 
1067
{
 
1068
  // Look up the translation of README.
 
1069
  std::string readme_file = ssprintf(HELPDIR "/%s", _("README"));
 
1070
  const char *encoding    = P_("Encoding of README|ISO_8859-1");
 
1071
 
 
1072
  // Deal with missing localized docs.
 
1073
  if(access(readme_file.c_str(), R_OK)!=0)
 
1074
    {
 
1075
      readme_file = HELPDIR "/README";
 
1076
      encoding    = "ISO_8859-1";
 
1077
    }
 
1078
 
 
1079
  setup_fileview(readme_file,
 
1080
                 encoding,
 
1081
                 _("User's Manual"),
 
1082
                 _("Read the full user's manual of aptitude"),
 
1083
                 _("Manual"));
 
1084
}
 
1085
 
 
1086
static void do_help_faq()
 
1087
{
 
1088
  setup_fileview(HELPDIR "/FAQ",
 
1089
                 NULL,
 
1090
                 _("FAQ"),
 
1091
                 _("View a list of frequently asked questions"),
 
1092
                 _("FAQ"));
 
1093
}
 
1094
 
 
1095
// news isn't translated since it's just a changelog.
 
1096
static void do_help_news()
 
1097
{
 
1098
  setup_fileview(HELPDIR "/NEWS",
 
1099
                 NULL,
 
1100
                 _("News"),
 
1101
                 ssprintf(_("View the important changes made in each version of %s"), PACKAGE),
 
1102
                 _("News"));
 
1103
}
 
1104
 
 
1105
static void do_kill_old_tmp(const std::string old_tmpdir)
 
1106
{
 
1107
  if(!aptitude::util::recursive_remdir(old_tmpdir))
 
1108
    show_message(ssprintf(_("Unable to remove the old temporary directory; you should remove %s by hand."), old_tmpdir.c_str()));
 
1109
}
 
1110
 
 
1111
static void cancel_kill_old_tmp(const std::string old_tmpdir)
 
1112
{
 
1113
  show_message(ssprintf(_("Will not remove %s; you should examine the files in it and remove them by hand."), old_tmpdir.c_str()));
 
1114
  aptcfg->Set(PACKAGE "::Ignore-Old-Tmp", "true");
 
1115
  apt_dumpcfg(PACKAGE);
 
1116
}
 
1117
 
 
1118
static void maybe_show_old_tmpdir_message()
 
1119
{
 
1120
  std::string tmpdir_path = get_homedir() + "/.aptitude/.tmp";
 
1121
 
 
1122
  if(aptcfg->FindB(PACKAGE "::Ignore-Old-Tmp", false))
 
1123
    {
 
1124
      // Watch for the reappearance of this directory.
 
1125
      if(access(tmpdir_path.c_str(), F_OK) != 0)
 
1126
        {
 
1127
          aptcfg->Set(PACKAGE "::Ignore-Old-Tmp", "false");
 
1128
          apt_dumpcfg(PACKAGE);
 
1129
        }
 
1130
 
 
1131
      return;
 
1132
    }
 
1133
 
 
1134
  // Remove it silently if it's empty.
 
1135
  if(rmdir(tmpdir_path.c_str()) == 0)
 
1136
    return;
 
1137
 
 
1138
  if(access(tmpdir_path.c_str(), F_OK) == 0)
 
1139
    prompt_yesno_popup(wrapbox(cw::fragf(_("It appears that a previous version of aptitude left files behind in %s.  These files are probably useless and safe to delete.%n%nDo you want to remove this directory and all its contents?  If you select \"No\", you will not see this message again."), tmpdir_path.c_str())),
 
1140
                       false,
 
1141
                       cw::util::arg(sigc::bind(sigc::ptr_fun(do_kill_old_tmp), tmpdir_path)),
 
1142
                       cw::util::arg(sigc::bind(sigc::ptr_fun(cancel_kill_old_tmp), tmpdir_path)));
 
1143
}
 
1144
 
 
1145
// There are some circular references because of the code to test
 
1146
// for consistency before doing a package run; the last routine called before
 
1147
// the program starts downloading will verify that the selections are
 
1148
// consistent and call its predecessors if they are not.
 
1149
//
 
1150
// (er, can I disentangle this by rearranging the routines?  I think maybe I
 
1151
//  can to some degree)
 
1152
 
 
1153
namespace
 
1154
{
 
1155
  void run_dpkg_with_cwidget_suspended(sigc::slot1<pkgPackageManager::OrderResult, int> f,
 
1156
                                       sigc::slot1<void, pkgPackageManager::OrderResult> k)
 
1157
  {
 
1158
    cw::toplevel::suspend();
 
1159
    pkgPackageManager::OrderResult rval = f(-1);
 
1160
    
 
1161
    if(rval != pkgPackageManager::Incomplete)
 
1162
      {
 
1163
        cerr << _("Press return to continue.\n");
 
1164
        int c = getchar();
 
1165
 
 
1166
        while(c != '\n'  && c != EOF)
 
1167
          c = getchar();
 
1168
      }
 
1169
 
 
1170
    // libapt-pkg likes to stomp on SIGINT and SIGQUIT.  Restore them
 
1171
    // here in the simplest possible way.
 
1172
    cw::toplevel::install_sighandlers();
 
1173
 
 
1174
    cw::toplevel::resume();
 
1175
 
 
1176
    k(rval);
 
1177
    return;
 
1178
  }
 
1179
}
 
1180
 
 
1181
namespace
 
1182
{
 
1183
  // Note that this is only safe if it's OK to copy the thunk in a
 
1184
  // background thread (i.e., it won't be invalidated by an object being
 
1185
  // destroyed in another thread).  In the special cases where we use
 
1186
  // this it should be all right.
 
1187
  void do_post_thunk(const safe_slot0<void> &thunk)
 
1188
  {
 
1189
    cw::toplevel::post_event(new aptitude::safe_slot_event(thunk));
 
1190
  }
 
1191
 
 
1192
  progress_with_destructor make_progress_bar()
 
1193
  {
 
1194
    progress_ref rval = gen_progress_bar();
 
1195
    return std::make_pair(rval->get_progress(),
 
1196
                          sigc::mem_fun(*rval.unsafe_get_ref(),
 
1197
                                        &progress::destroy));
 
1198
  }
 
1199
}
 
1200
 
 
1201
void install_or_remove_packages()
 
1202
{
 
1203
  boost::shared_ptr<download_install_manager> m =
 
1204
    boost::make_shared<download_install_manager>(false, sigc::ptr_fun(&run_dpkg_with_cwidget_suspended));
 
1205
 
 
1206
  m->post_forget_new_hook.connect(package_states_changed.make_slot());
 
1207
 
 
1208
  std::pair<download_signal_log *, download_list_ref>
 
1209
    download_log_pair = gen_download_progress(false, false,
 
1210
                                              _("Downloading packages"),
 
1211
                                              _("View the progress of the package download"),
 
1212
                                              _("Package Download"));
 
1213
 
 
1214
  ui_download_manager *uim = new ui_download_manager(m,
 
1215
                                                     download_log_pair.first,
 
1216
                                                     download_log_pair.second,
 
1217
                                                     sigc::ptr_fun(&make_progress_bar),
 
1218
                                                     &do_post_thunk);
 
1219
 
 
1220
  download_log_pair.second->cancelled.connect(sigc::mem_fun(*uim, &ui_download_manager::aborted));
 
1221
 
 
1222
  uim->download_starts.connect(sigc::bind(sigc::ptr_fun(&ui_start_download), true));
 
1223
  uim->download_stops.connect(sigc::ptr_fun(&ui_stop_download));
 
1224
 
 
1225
  uim->download_complete.connect(install_finished.make_slot());
 
1226
  uim->start();
 
1227
}
 
1228
 
 
1229
/** Make sure that no trust violations are about to be committed.  If
 
1230
 *  any are, warn the user and give them a chance to fix the
 
1231
 *  situation.
 
1232
 *
 
1233
 *  Should this warning be displayed when a package is selected instead?
 
1234
 */
 
1235
static void check_package_trust()
 
1236
{
 
1237
  vector<pkgCache::VerIterator> untrusted;
 
1238
 
 
1239
  for(pkgCache::PkgIterator pkg=(*apt_cache_file)->PkgBegin();
 
1240
      !pkg.end(); ++pkg)
 
1241
    {
 
1242
      pkgDepCache::StateCache &state=(*apt_cache_file)[pkg];
 
1243
 
 
1244
      if(state.Install())
 
1245
        {
 
1246
          pkgCache::VerIterator curr=pkg.CurrentVer();
 
1247
          pkgCache::VerIterator cand=state.InstVerIter(*apt_cache_file);
 
1248
 
 
1249
          if((curr.end() || package_trusted(curr)) &&
 
1250
             !package_trusted(cand))
 
1251
            untrusted.push_back(cand);
 
1252
        }
 
1253
    }
 
1254
 
 
1255
  if(!untrusted.empty())
 
1256
    {
 
1257
      vector<cw::fragment *> frags;
 
1258
 
 
1259
      frags.push_back(wrapbox(cw::fragf(_("%BWARNING%b: untrusted versions of the following packages will be installed!%n%n"
 
1260
                                      "Untrusted packages could %Bcompromise your system's security%b.  "
 
1261
                                      "You should only proceed with the installation if you are certain that this is what you want to do.%n%n"), "Error")));
 
1262
 
 
1263
      for(vector<pkgCache::VerIterator>::const_iterator i=untrusted.begin();
 
1264
          i!=untrusted.end(); ++i)
 
1265
        frags.push_back(clipbox(cw::fragf(_("  %S*%N %s [version %s]%n"),
 
1266
                                      "Bullet",
 
1267
                                      i->ParentPkg().Name(), i->VerStr())));
 
1268
 
 
1269
      main_stacked->add_visible_widget(cw::dialogs::yesno(cw::sequence_fragment(frags),
 
1270
                                                       cw::util::arg(sigc::ptr_fun(install_or_remove_packages)),
 
1271
                                                       W_("Really Continue"),
 
1272
                                                       NULL,
 
1273
                                                       W_("Abort Installation"),
 
1274
                                                       cw::get_style("TrustWarning"),
 
1275
                                                       true,
 
1276
                                                       false),
 
1277
                                       true);
 
1278
    }
 
1279
  else
 
1280
    install_or_remove_packages();
 
1281
}
 
1282
 
 
1283
namespace
 
1284
{
 
1285
  void actually_do_package_run();
 
1286
}
 
1287
 
 
1288
static void reset_preview()
 
1289
{
 
1290
  active_preview=NULL;
 
1291
  active_preview_tree=NULL;
 
1292
}
 
1293
 
 
1294
// One word: ewwwwwwww.  I REALLY need to get my act together on the
 
1295
// view-customization stuff.
 
1296
static void do_show_preview()
 
1297
{
 
1298
  if(!active_preview_tree.valid())
 
1299
    {
 
1300
      eassert(!active_preview.valid());
 
1301
 
 
1302
      pkg_grouppolicy_factory *grp=NULL;
 
1303
      std::string grpstr;
 
1304
 
 
1305
      if(aptcfg->Exists(PACKAGE "::UI::Default-Preview-Grouping"))
 
1306
        {
 
1307
          grpstr=aptcfg->Find(PACKAGE "::UI::Default-Preview-Grouping");
 
1308
          grp=parse_grouppolicy(grpstr);
 
1309
        }
 
1310
 
 
1311
      //if(!grp && aptcfg->Exists(PACKAGE "::UI::Default-Grouping"))
 
1312
        //{
 
1313
        //  grpstr=aptcfg->Find(PACKAGE "::UI::Default-Grouping");
 
1314
        //  grp=parse_grouppolicy(grpstr);
 
1315
        //}
 
1316
 
 
1317
      if(!grp)
 
1318
        {
 
1319
          grpstr="action";
 
1320
          grp=new pkg_grouppolicy_mode_factory(new pkg_grouppolicy_end_factory);
 
1321
        }
 
1322
 
 
1323
      if(aptcfg->Exists(PACKAGE "::UI::Preview-Limit"))
 
1324
        active_preview_tree=pkg_tree::create(grpstr.c_str(), grp, cw::util::transcode(aptcfg->Find(PACKAGE "::UI::Preview-Limit").c_str()));
 
1325
      else
 
1326
        active_preview_tree=pkg_tree::create(grpstr.c_str(), grp);
 
1327
 
 
1328
      active_preview=make_default_view(active_preview_tree,
 
1329
                                       &active_preview_tree->selected_signal,
 
1330
                                       &active_preview_tree->selected_desc_signal,
 
1331
                                       true,
 
1332
                                       true);
 
1333
 
 
1334
      active_preview->destroyed.connect(sigc::ptr_fun(reset_preview));
 
1335
      active_preview->connect_key("DoInstallRun",
 
1336
                                  &cw::config::global_bindings,
 
1337
                                  sigc::ptr_fun(actually_do_package_run));
 
1338
      add_main_widget(active_preview, _("Preview of package installation"),
 
1339
                      _("View and/or adjust the actions that will be performed"),
 
1340
                      _("Preview"));
 
1341
 
 
1342
      progress_ref p=gen_progress_bar();
 
1343
      active_preview_tree->build_tree(*p->get_progress().unsafe_get_ref());
 
1344
      p->destroy();
 
1345
    }
 
1346
  else
 
1347
    {
 
1348
      eassert(active_preview.valid());
 
1349
      active_preview->show();
 
1350
    }
 
1351
}
 
1352
 
 
1353
static void do_keep_all()
 
1354
{
 
1355
  auto_ptr<undo_group> undo(new apt_undo_group);
 
1356
 
 
1357
  aptitudeDepCache::action_group group(*apt_cache_file, undo.get());
 
1358
 
 
1359
  for(pkgCache::PkgIterator i=(*apt_cache_file)->PkgBegin();
 
1360
      !i.end(); ++i)
 
1361
    (*apt_cache_file)->mark_keep(i, false, false, undo.get());
 
1362
 
 
1363
  if(!undo.get()->empty())
 
1364
    apt_undos->add_item(undo.release());
 
1365
}
 
1366
 
 
1367
static void fixer_dialog_done()
 
1368
{
 
1369
  if(active_preview_tree.valid())
 
1370
    active_preview_tree->build_tree();
 
1371
  do_package_run_or_show_preview();
 
1372
}
 
1373
 
 
1374
static void install_fixer_dialog()
 
1375
{
 
1376
  cw::widget_ref w=make_solution_dialog();
 
1377
  w->destroyed.connect(sigc::ptr_fun(fixer_dialog_done));
 
1378
  popup_widget(w, true);
 
1379
}
 
1380
 
 
1381
// FIXME: blocks.
 
1382
static void auto_fix_broken()
 
1383
{
 
1384
  undo_group *undo=new apt_undo_group;
 
1385
 
 
1386
  try
 
1387
    {
 
1388
      eassert(resman != NULL);
 
1389
      eassert(resman->resolver_exists());
 
1390
 
 
1391
      aptitude_solution sol = resman->get_solution(resman->get_selected_solution(), 0);
 
1392
 
 
1393
      (*apt_cache_file)->apply_solution(sol, undo);
 
1394
 
 
1395
      cw::widget_ref d = cw::dialogs::ok(cw::fragf("%s%n%n%F",
 
1396
                                           _("Some packages were broken and have been fixed:"),
 
1397
                                           solution_fragment(sol)),
 
1398
                                     NULL);
 
1399
 
 
1400
      popup_widget(d, true);
 
1401
    }
 
1402
  catch(NoMoreSolutions)
 
1403
    {
 
1404
      show_message(_("No solution to these dependency problems exists!"),
 
1405
                   NULL,
 
1406
                   cw::get_style("Error"));
 
1407
    }
 
1408
  catch(NoMoreTime)
 
1409
    {
 
1410
      show_message(cw::fragf(_("Ran out of time while trying to resolve dependencies (press \"%s\" to try harder)"),
 
1411
                         cw::config::global_bindings.readable_keyname("NextSolution").c_str()),
 
1412
                   NULL,
 
1413
                   cw::get_style("Error"));
 
1414
    }
 
1415
 
 
1416
  if(!undo->empty())
 
1417
    apt_undos->add_item(undo);
 
1418
  else
 
1419
    delete undo;
 
1420
 
 
1421
  if(active_preview_tree.valid())
 
1422
    active_preview_tree->build_tree();
 
1423
}
 
1424
 
 
1425
 
 
1426
//  Huge FIXME: the preview interacts badly with the menu.  This can be solved
 
1427
// in a couple ways, including having the preview be a popup dialog -- the best
 
1428
// thing IMO, though, would be to somehow allow particular widgets to override
 
1429
// the meaning of global commands.  This needs a little thought, though.  (fake
 
1430
// keys?  BLEACH)
 
1431
static void actually_do_package_run_post_essential_check()
 
1432
{
 
1433
  if(apt_cache_file)
 
1434
    {
 
1435
      if(!active_download)
 
1436
        {
 
1437
          // whatever we call will chain to the next appropriate
 
1438
          // routine.
 
1439
          if((*apt_cache_file)->BrokenCount()>0)
 
1440
            {
 
1441
              if(_config->FindB(PACKAGE "::Auto-Fix-Broken", true))
 
1442
                {
 
1443
                  auto_fix_broken();
 
1444
                  do_show_preview();
 
1445
                }
 
1446
              else
 
1447
                install_fixer_dialog();
 
1448
 
 
1449
              return;
 
1450
            }
 
1451
 
 
1452
          if(getuid()==0  || !aptcfg->FindB(PACKAGE "::Warn-Not-Root", true))
 
1453
            check_package_trust();
 
1454
          else
 
1455
            {
 
1456
              popup_widget(cw::dialogs::yesno(wrapbox(cw::text_fragment(_("Installing/removing packages requires administrative privileges, which you currently do not have.  Would you like to change to the root account?"))),
 
1457
                                           cw::util::arg(sigc::bind(sigc::ptr_fun(&do_su_to_root),
 
1458
                                                      "-i")),
 
1459
                                           W_("Become root"),
 
1460
                                           cw::util::arg(sigc::ptr_fun(&check_package_trust)),
 
1461
                                           W_("Don't become root"),
 
1462
                                           cw::get_style("Error")));
 
1463
            }
 
1464
        }
 
1465
      else
 
1466
        show_message(_("A package-list update or install run is already taking place."), NULL, cw::get_style("Error"));
 
1467
    }
 
1468
}
 
1469
 
 
1470
namespace
 
1471
{
 
1472
  void actually_do_package_run_finish_delete_essential(const std::wstring &response)
 
1473
  {
 
1474
    if(response == cw::util::transcode(_(confirm_delete_essential_str)) ||
 
1475
       response == cw::util::transcode(confirm_delete_essential_str))
 
1476
      actually_do_package_run_post_essential_check();
 
1477
  }
 
1478
 
 
1479
  void actually_do_package_run()
 
1480
  {
 
1481
    if(apt_cache_file)
 
1482
      {
 
1483
        // Failsafe check to ensure that we aren't deleting any
 
1484
        // Essential packages.
 
1485
        //
 
1486
        // \todo We should remember which ones the user already
 
1487
        // confirmed and not ask twice.
 
1488
        std::vector<pkgCache::PkgIterator> deleted_essential, broken_essential;
 
1489
 
 
1490
        for(pkgCache::PkgIterator pkg = (*apt_cache_file)->PkgBegin();
 
1491
            !pkg.end(); ++pkg)
 
1492
          {
 
1493
            pkgDepCache::StateCache &state = (*apt_cache_file)[pkg];
 
1494
 
 
1495
            if((pkg->Flags & pkgCache::Flag::Essential) &&
 
1496
               state.Status != 2)
 
1497
              {
 
1498
                if(state.Delete())
 
1499
                  deleted_essential.push_back(pkg);
 
1500
                if(state.InstBroken())
 
1501
                  broken_essential.push_back(pkg);
 
1502
              }
 
1503
          }
 
1504
 
 
1505
        if(deleted_essential.empty() && broken_essential.empty())
 
1506
          actually_do_package_run_post_essential_check();
 
1507
        else
 
1508
          {
 
1509
            // We reuse the command line's strings here so that
 
1510
            // translators don't need to add new translations.
 
1511
            std::vector<cw::fragment *> fragments;
 
1512
            if(!deleted_essential.empty())
 
1513
              {
 
1514
                fragments.push_back(wrapbox(cw::text_fragment(_("The following ESSENTIAL packages will be REMOVED!\n"))));
 
1515
 
 
1516
                for(std::vector<pkgCache::PkgIterator>::const_iterator it =
 
1517
                      deleted_essential.begin(); it != deleted_essential.end(); ++it)
 
1518
                  {
 
1519
                    fragments.push_back(cw::fragf("  %S*%N %s%n",
 
1520
                                                  "Bullet",
 
1521
                                                  it->Name()));
 
1522
                  }
 
1523
 
 
1524
                fragments.push_back(cw::newline_fragment());
 
1525
              }
 
1526
 
 
1527
            if(!broken_essential.empty())
 
1528
              {
 
1529
                fragments.push_back(cw::text_fragment(_("The following ESSENTIAL packages will be BROKEN by this action:\n")));
 
1530
 
 
1531
                for(std::vector<pkgCache::PkgIterator>::const_iterator it =
 
1532
                      broken_essential.begin(); it != broken_essential.end(); ++it)
 
1533
                  {
 
1534
                    fragments.push_back(cw::fragf("  %S*%N %s%n",
 
1535
                                                  "Bullet",
 
1536
                                                  it->Name()));
 
1537
                  }
 
1538
 
 
1539
                fragments.push_back(cw::newline_fragment());
 
1540
              }
 
1541
 
 
1542
            fragments.push_back(wrapbox(cw::text_fragment(_("WARNING: Performing this action will probably cause your system to break!\n         Do NOT continue unless you know EXACTLY what you are doing!\n"))));
 
1543
            fragments.push_back(wrapbox(cw::fragf(_("To continue, type the phrase \"%s\":\n"), confirm_delete_essential_str)));
 
1544
 
 
1545
            cw::widget_ref w = cw::dialogs::string(cw::sequence_fragment(fragments),
 
1546
                                                   L"",
 
1547
                                                   cw::util::arg(sigc::ptr_fun(&actually_do_package_run_finish_delete_essential)),
 
1548
                                                   NULL,
 
1549
                                                   NULL,
 
1550
                                                   NULL,
 
1551
                                                   cw::style_attrs_flip(A_REVERSE));
 
1552
            w->show_all();
 
1553
            popup_widget(w);
 
1554
          }
 
1555
      }
 
1556
  }
 
1557
}
 
1558
 
 
1559
void do_package_run_or_show_preview()
 
1560
{
 
1561
  // UI: check that something will actually be done first.
 
1562
  bool some_action_happening=false;
 
1563
  bool some_non_simple_keep_happening=false;
 
1564
  for(pkgCache::PkgIterator i=(*apt_cache_file)->PkgBegin(); !i.end(); ++i)
 
1565
    {
 
1566
      pkg_action_state state = find_pkg_state(i, *apt_cache_file);
 
1567
 
 
1568
      if(state!=pkg_unchanged)
 
1569
        {
 
1570
          some_action_happening=true;
 
1571
 
 
1572
          if(!(state==pkg_hold && !(*apt_cache_file)->is_held(i)))
 
1573
            some_non_simple_keep_happening=true;
 
1574
        }
 
1575
 
 
1576
      if(some_action_happening && some_non_simple_keep_happening)
 
1577
        break;
 
1578
    }
 
1579
 
 
1580
  if(!some_action_happening)
 
1581
    {
 
1582
      show_message(_("No packages are scheduled to be installed, removed, or upgraded."));
 
1583
      return;
 
1584
    }
 
1585
  else if(!some_non_simple_keep_happening &&
 
1586
          !aptcfg->FindB(PACKAGE "::Allow-Null-Upgrade", false))
 
1587
    {
 
1588
      show_message(_("No packages will be installed, removed or upgraded.  "
 
1589
                     "Some packages could be upgraded, but you have not chosen to upgrade them.  "
 
1590
                     "Type \"U\" to prepare an upgrade."));
 
1591
      return;
 
1592
    }
 
1593
 
 
1594
  if(apt_cache_file)
 
1595
    {
 
1596
      if(!active_preview.valid() || !active_preview->get_visible())
 
1597
        {
 
1598
          if(aptcfg->FindB(PACKAGE "::Display-Planned-Action", true))
 
1599
            do_show_preview();
 
1600
          else
 
1601
            actually_do_package_run();
 
1602
        }
 
1603
      else
 
1604
        {
 
1605
          active_preview_tree->build_tree();
 
1606
          // We need to rebuild the tree since this is called after a
 
1607
          // broken-fixing operation.  This feels like a hack, though..
 
1608
          active_preview->show();
 
1609
        }
 
1610
    }
 
1611
}
 
1612
 
 
1613
static bool can_start_download()
 
1614
{
 
1615
  return !active_download;
 
1616
}
 
1617
 
 
1618
static bool can_start_download_and_install()
 
1619
{
 
1620
  return !active_download && apt_cache_file != NULL;
 
1621
}
 
1622
 
 
1623
void do_package_run()
 
1624
{
 
1625
  if(apt_cache_file)
 
1626
    {
 
1627
      if(active_preview_tree.valid() && active_preview_tree->get_visible())
 
1628
        actually_do_package_run();
 
1629
      else if((*apt_cache_file)->BrokenCount()>0)
 
1630
        {
 
1631
          if(aptcfg->FindB(PACKAGE "::Auto-Fix-Broken", true))
 
1632
            {
 
1633
              auto_fix_broken();
 
1634
              do_package_run_or_show_preview();
 
1635
            }
 
1636
          else
 
1637
            install_fixer_dialog();
 
1638
        }
 
1639
      else
 
1640
        do_package_run_or_show_preview();
 
1641
    }
 
1642
}
 
1643
 
 
1644
static void lists_autoclean_msg(boost::weak_ptr<download_update_manager> m_weak)
 
1645
{
 
1646
  boost::shared_ptr<download_update_manager> m(m_weak);
 
1647
 
 
1648
  if(m.get() == NULL)
 
1649
    return;
 
1650
 
 
1651
  cw::widget_ref msg = cw::center::create(cw::frame::create(cw::label::create(_("Deleting obsolete downloaded files"))));
 
1652
  m->post_autoclean_hook.connect(sigc::mem_fun(msg.unsafe_get_ref(),
 
1653
                                               &cw::widget::destroy));
 
1654
 
 
1655
  popup_widget(msg);
 
1656
  cw::toplevel::tryupdate();
 
1657
}
 
1658
 
 
1659
void really_do_update_lists()
 
1660
{
 
1661
  boost::shared_ptr<download_update_manager> m = boost::make_shared<download_update_manager>();
 
1662
  m->pre_autoclean_hook.connect(sigc::bind(sigc::ptr_fun(lists_autoclean_msg),
 
1663
                                           boost::weak_ptr<download_update_manager>(m)));
 
1664
  m->post_forget_new_hook.connect(package_states_changed.make_slot());
 
1665
 
 
1666
  std::pair<download_signal_log *, download_list_ref>
 
1667
    download_log_pair = gen_download_progress(false, true,
 
1668
                                              _("Updating package lists"),
 
1669
                                              _("View the progress of the package list update"),
 
1670
                                              _("List Update"));
 
1671
 
 
1672
  ui_download_manager *uim = new ui_download_manager(m,
 
1673
                                                     download_log_pair.first,
 
1674
                                                     download_log_pair.second,
 
1675
                                                     sigc::ptr_fun(&make_progress_bar),
 
1676
                                                     &do_post_thunk);
 
1677
 
 
1678
  download_log_pair.second->cancelled.connect(sigc::mem_fun(*uim, &ui_download_manager::aborted));
 
1679
 
 
1680
  uim->download_starts.connect(sigc::bind(sigc::ptr_fun(&ui_start_download), false));
 
1681
  uim->download_stops.connect(sigc::ptr_fun(&ui_stop_download));
 
1682
 
 
1683
  uim->download_complete.connect(update_finished.make_slot());
 
1684
  uim->start();
 
1685
}
 
1686
 
 
1687
void do_update_lists()
 
1688
{
 
1689
  if(!active_download)
 
1690
    {
 
1691
      if(getuid()==0 || !aptcfg->FindB(PACKAGE "::Warn-Not-Root", true))
 
1692
        really_do_update_lists();
 
1693
      else
 
1694
        {
 
1695
          popup_widget(cw::dialogs::yesno(wrapbox(cw::text_fragment(_("Updating the package lists requires administrative privileges, which you currently do not have.  Would you like to change to the root account?"))),
 
1696
                                       cw::util::arg(sigc::bind(sigc::ptr_fun(&do_su_to_root),
 
1697
                                                      "-u")),
 
1698
                                       W_("Become root"),
 
1699
                                       cw::util::arg(sigc::ptr_fun(&really_do_update_lists)),
 
1700
                                       W_("Don't become root"),
 
1701
                                       cw::get_style("Error")));
 
1702
        }
 
1703
    }
 
1704
  else
 
1705
    show_message(_("A package-list update or install run is already taking place."), NULL, cw::get_style("Error"));
 
1706
}
 
1707
 
 
1708
static void do_sweep()
 
1709
{
 
1710
  add_main_widget(cmine::create(), _("Minesweeper"), _("Waste time trying to find mines"), _("Minesweeper"));
 
1711
}
 
1712
 
 
1713
static void really_do_clean()
 
1714
{
 
1715
  if(active_download)
 
1716
    // Erk!  That's weird!
 
1717
    _error->Error(_("Cleaning while a download is in progress is not allowed"));
 
1718
  else
 
1719
    {
 
1720
      cw::widget_ref msg=cw::center::create(cw::frame::create(cw::label::create(_("Deleting downloaded files"))));
 
1721
      msg->show_all();
 
1722
      popup_widget(msg);
 
1723
      cw::toplevel::tryupdate();
 
1724
 
 
1725
      if(aptcfg)
 
1726
        {
 
1727
          pkgAcquire fetcher;
 
1728
          fetcher.Clean(aptcfg->FindDir("Dir::Cache::archives"));
 
1729
          fetcher.Clean(aptcfg->FindDir("Dir::Cache::archives")+"partial/");
 
1730
        }
 
1731
 
 
1732
      msg->destroy();
 
1733
 
 
1734
      show_message(_("Downloaded package files have been deleted"));
 
1735
    }
 
1736
}
 
1737
 
 
1738
void do_clean()
 
1739
{
 
1740
  if(getuid()==0 || !aptcfg->FindB(PACKAGE "::Warn-Not-Root", true))
 
1741
    really_do_clean();
 
1742
  else
 
1743
    {
 
1744
          popup_widget(cw::dialogs::yesno(wrapbox(cw::text_fragment(_("Cleaning the package cache requires administrative privileges, which you currently do not have.  Would you like to change to the root account?"))),
 
1745
                                       cw::util::arg(sigc::bind(sigc::ptr_fun(&do_su_to_root),
 
1746
                                                      "--clean-on-startup")),
 
1747
                                       W_("Become root"),
 
1748
                                       cw::util::arg(sigc::ptr_fun(&really_do_update_lists)),
 
1749
                                       W_("Don't become root"),
 
1750
                                       cw::get_style("Error")));
 
1751
    }
 
1752
}
 
1753
 
 
1754
// Ok, this is, uh, weird.  Erase has to be overridden to at least
 
1755
// call unlink()
 
1756
//
 
1757
// Should I list what I'm doing like apt-get does?
 
1758
class my_cleaner:public pkgArchiveCleaner
 
1759
{
 
1760
  long total_size;
 
1761
protected:
 
1762
  virtual void Erase(const char *file,
 
1763
                     string pkg,
 
1764
                     string ver,
 
1765
                     struct stat &stat)
 
1766
  {
 
1767
    if(unlink(file)==0)
 
1768
      total_size+=stat.st_size;
 
1769
  }
 
1770
public:
 
1771
  my_cleaner();
 
1772
  long get_total_size() {return total_size;}
 
1773
};
 
1774
 
 
1775
// g++ bug?  If I include this implementation above in the class
 
1776
// def'n, it never gets called!
 
1777
my_cleaner::my_cleaner()
 
1778
  :total_size(0)
 
1779
{
 
1780
}
 
1781
 
 
1782
static bool do_autoclean_enabled()
 
1783
{
 
1784
  return apt_cache_file != NULL;
 
1785
}
 
1786
 
 
1787
static void really_do_autoclean()
 
1788
{
 
1789
  if(apt_cache_file == NULL)
 
1790
    _error->Error(_("The apt cache file is not available; cannot auto-clean."));
 
1791
  else if(active_download)
 
1792
    // Erk!  That's weird!
 
1793
    _error->Error(_("Cleaning while a download is in progress is not allowed"));
 
1794
  else
 
1795
    {
 
1796
      cw::widget_ref msg=cw::center::create(cw::frame::create(cw::label::create(_("Deleting obsolete downloaded files"))));
 
1797
      msg->show_all();
 
1798
      popup_widget(msg);
 
1799
      cw::toplevel::tryupdate();
 
1800
 
 
1801
      long cleaned_size=0;
 
1802
 
 
1803
      if(aptcfg)
 
1804
        {
 
1805
          my_cleaner cleaner;
 
1806
 
 
1807
          cleaner.Go(aptcfg->FindDir("Dir::Cache::archives"), *apt_cache_file);
 
1808
          cleaner.Go(aptcfg->FindDir("Dir::Cache::archives")+"partial/",
 
1809
                     *apt_cache_file);
 
1810
 
 
1811
          cleaned_size=cleaner.get_total_size();
 
1812
        }
 
1813
 
 
1814
      msg->destroy();
 
1815
 
 
1816
      show_message(ssprintf(_("Obsolete downloaded package files have been deleted, freeing %sB of disk space."),
 
1817
                            SizeToStr(cleaned_size).c_str()));
 
1818
    }
 
1819
}
 
1820
 
 
1821
void do_autoclean()
 
1822
{
 
1823
  if(getuid()==0 || !aptcfg->FindB(PACKAGE "::Warn-Not-Root", true))
 
1824
    really_do_autoclean();
 
1825
  else
 
1826
    {
 
1827
          popup_widget(cw::dialogs::yesno(wrapbox(cw::text_fragment(_("Deleting obsolete files requires administrative privileges, which you currently do not have.  Would you like to change to the root account?"))),
 
1828
                                       cw::util::arg(sigc::bind(sigc::ptr_fun(&do_su_to_root),
 
1829
                                                      "--autoclean-on-startup")),
 
1830
                                       W_("Become root"),
 
1831
                                       cw::util::arg(sigc::ptr_fun(&really_do_update_lists)),
 
1832
                                       W_("Don't become root"),
 
1833
                                       cw::get_style("Error")));
 
1834
    }
 
1835
}
 
1836
 
 
1837
static bool do_mark_upgradable_enabled()
 
1838
{
 
1839
  return apt_cache_file != NULL;
 
1840
}
 
1841
 
 
1842
static void do_mark_upgradable()
 
1843
{
 
1844
  if(apt_cache_file)
 
1845
    {
 
1846
      undo_group *undo=new apt_undo_group;
 
1847
 
 
1848
      (*apt_cache_file)->mark_all_upgradable(true, true, undo);
 
1849
 
 
1850
      if(!undo->empty())
 
1851
        apt_undos->add_item(undo);
 
1852
      else
 
1853
        delete undo;
 
1854
 
 
1855
      package_states_changed();
 
1856
    }
 
1857
}
 
1858
 
 
1859
static bool forget_new_enabled()
 
1860
{
 
1861
  if(!apt_cache_file)
 
1862
    return false;
 
1863
  else
 
1864
    return (*apt_cache_file)->get_new_package_count()>0;
 
1865
}
 
1866
 
 
1867
void do_forget_new()
 
1868
{
 
1869
  if(apt_cache_file)
 
1870
    {
 
1871
      undoable *undo=NULL;
 
1872
      (*apt_cache_file)->forget_new(&undo);
 
1873
      if(undo)
 
1874
        apt_undos->add_item(undo);
 
1875
 
 
1876
      package_states_changed();
 
1877
    }
 
1878
}
 
1879
 
 
1880
#ifdef WITH_RELOAD_CACHE
 
1881
static void do_reload_cache()
 
1882
{
 
1883
  progress_ref p = gen_progress_bar();
 
1884
  apt_reload_cache(p->get_progress().unsafe_get_ref(), true);
 
1885
  p->destroy();
 
1886
}
 
1887
#endif
 
1888
 
 
1889
static void start_solution_calculation();
 
1890
 
 
1891
class interactive_continuation : public resolver_manager::background_continuation
 
1892
{
 
1893
  /** The manager associated with this continuation; usually resman. */
 
1894
  resolver_manager *manager;
 
1895
 
 
1896
  /** Indicate that a new solution is available by invoking the
 
1897
   *  selection_changed signal.
 
1898
   */
 
1899
  class success_event : public cw::toplevel::event
 
1900
  {
 
1901
    resolver_manager *manager;
 
1902
  public:
 
1903
    success_event(resolver_manager *_manager)
 
1904
      :manager(_manager)
 
1905
    {
 
1906
    }
 
1907
 
 
1908
    void dispatch()
 
1909
    {
 
1910
      manager->state_changed();
 
1911
    }
 
1912
  };
 
1913
 
 
1914
  class no_more_solutions_event : public cw::toplevel::event
 
1915
  {
 
1916
    resolver_manager *manager;
 
1917
  public:
 
1918
    no_more_solutions_event(resolver_manager *_manager)
 
1919
      :manager(_manager)
 
1920
    {
 
1921
    }
 
1922
 
 
1923
    void dispatch()
 
1924
    {
 
1925
      resolver_manager::state st = manager->state_snapshot();
 
1926
 
 
1927
      if(st.selected_solution == st.generated_solutions)
 
1928
        manager->select_previous_solution();
 
1929
      show_message(_("No more solutions."));
 
1930
 
 
1931
      manager->state_changed();
 
1932
    }
 
1933
  };
 
1934
 
 
1935
  class solution_search_aborted_event : public cw::toplevel::event
 
1936
  {
 
1937
    resolver_manager *manager;
 
1938
    std::string msg;
 
1939
  public:
 
1940
    solution_search_aborted_event(resolver_manager *_manager,
 
1941
                                  std::string _msg)
 
1942
      : manager(_manager), msg(_msg)
 
1943
    {
 
1944
    }
 
1945
 
 
1946
    void dispatch()
 
1947
    {
 
1948
      resolver_manager::state st = manager->state_snapshot();
 
1949
 
 
1950
      if(st.selected_solution == st.generated_solutions)
 
1951
        manager->select_previous_solution();
 
1952
      show_message(ssprintf("Fatal error in dependency resolver.  You can continue searching, but some solutions might be impossible to generate.\n\n%s",
 
1953
                            msg.c_str()));
 
1954
 
 
1955
      manager->state_changed();
 
1956
    }
 
1957
  };
 
1958
public:
 
1959
  interactive_continuation(resolver_manager *_manager)
 
1960
    :manager(_manager)
 
1961
  {
 
1962
  }
 
1963
 
 
1964
  void success(const aptitude_solution &sol)
 
1965
  {
 
1966
    cw::toplevel::post_event(new success_event(manager));
 
1967
  }
 
1968
 
 
1969
  void no_more_solutions()
 
1970
  {
 
1971
    cw::toplevel::post_event(new no_more_solutions_event(manager));
 
1972
  }
 
1973
 
 
1974
  void no_more_time()
 
1975
  {
 
1976
    start_solution_calculation();
 
1977
  }
 
1978
 
 
1979
  void interrupted()
 
1980
  {
 
1981
  }
 
1982
 
 
1983
  void aborted(const std::string &errmsg)
 
1984
  {
 
1985
    cw::toplevel::post_event(new solution_search_aborted_event(manager, errmsg));
 
1986
  }
 
1987
};
 
1988
 
 
1989
void cwidget_resolver_trampoline(const sigc::slot<void> &f)
 
1990
{
 
1991
  f();
 
1992
}
 
1993
 
 
1994
// If the current solution pointer is past the end of the solution
 
1995
// list, queue up a calculation for it in the background thread.
 
1996
static void start_solution_calculation()
 
1997
{
 
1998
  resman->maybe_start_solution_calculation(boost::make_shared<interactive_continuation>(resman),
 
1999
                                           &cwidget_resolver_trampoline);
 
2000
}
 
2001
 
 
2002
static void do_connect_resolver_callback()
 
2003
{
 
2004
  resman->state_changed.connect(sigc::ptr_fun(&start_solution_calculation));
 
2005
  // We may have missed a signal before making the connection:
 
2006
  start_solution_calculation();
 
2007
  resman->state_changed.connect(sigc::ptr_fun(&cw::toplevel::update));
 
2008
}
 
2009
 
 
2010
static bool do_next_solution_enabled()
 
2011
{
 
2012
  if(resman == NULL)
 
2013
    return false;
 
2014
 
 
2015
  resolver_manager::state state = resman->state_snapshot();
 
2016
 
 
2017
  return
 
2018
    state.selected_solution < state.generated_solutions &&
 
2019
    !(state.selected_solution + 1 == state.generated_solutions &&
 
2020
      state.solutions_exhausted);
 
2021
}
 
2022
 
 
2023
void do_next_solution()
 
2024
{
 
2025
  if(!do_next_solution_enabled())
 
2026
    beep();
 
2027
  else
 
2028
    {
 
2029
      // If an error was encountered, pressing "next solution"
 
2030
      // skips it.
 
2031
      resman->discard_error_information();
 
2032
      resman->select_next_solution();
 
2033
    }
 
2034
}
 
2035
 
 
2036
static bool do_previous_solution_enabled()
 
2037
{
 
2038
  if(resman == NULL)
 
2039
    return false;
 
2040
 
 
2041
  resolver_manager::state state = resman->state_snapshot();
 
2042
 
 
2043
  return state.selected_solution > 0;
 
2044
}
 
2045
 
 
2046
void do_previous_solution()
 
2047
{
 
2048
  if(!do_previous_solution_enabled())
 
2049
    beep();
 
2050
  else
 
2051
    resman->select_previous_solution();
 
2052
}
 
2053
 
 
2054
static bool do_first_solution_enabled()
 
2055
{
 
2056
  if(resman == NULL)
 
2057
    return false;
 
2058
 
 
2059
  resolver_manager::state state = resman->state_snapshot();
 
2060
 
 
2061
  return state.resolver_exists && state.selected_solution > 0;
 
2062
}
 
2063
 
 
2064
static void do_first_solution()
 
2065
{
 
2066
  if(!do_first_solution_enabled())
 
2067
    beep();
 
2068
  else
 
2069
    resman->select_solution(0);
 
2070
}
 
2071
 
 
2072
static bool do_last_solution_enabled()
 
2073
{
 
2074
  if(resman == NULL)
 
2075
    return false;
 
2076
 
 
2077
  resolver_manager::state state = resman->state_snapshot();
 
2078
 
 
2079
  return state.resolver_exists && state.selected_solution + 1 < state.generated_solutions;
 
2080
}
 
2081
 
 
2082
static void do_last_solution()
 
2083
{
 
2084
  if(resman == NULL)
 
2085
    {
 
2086
      beep();
 
2087
      return;
 
2088
    }
 
2089
 
 
2090
  resolver_manager::state state = resman->state_snapshot();
 
2091
 
 
2092
  if(!(state.resolver_exists && state.selected_solution + 1 < state.generated_solutions))
 
2093
    beep();
 
2094
  else
 
2095
    resman->select_solution(state.generated_solutions - 1);
 
2096
}
 
2097
 
 
2098
static bool do_apply_solution_enabled_from_state(const resolver_manager::state &state)
 
2099
{
 
2100
  return
 
2101
    state.resolver_exists &&
 
2102
    state.selected_solution >= 0 &&
 
2103
    state.selected_solution < state.generated_solutions;
 
2104
}
 
2105
 
 
2106
static bool do_apply_solution_enabled()
 
2107
{
 
2108
  if(resman == NULL)
 
2109
    return false;
 
2110
 
 
2111
  resolver_manager::state state = resman->state_snapshot();
 
2112
  return do_apply_solution_enabled_from_state(state);
 
2113
}
 
2114
 
 
2115
void do_apply_solution()
 
2116
{
 
2117
  if(!apt_cache_file)
 
2118
    return;
 
2119
 
 
2120
  resolver_manager::state state = resman->state_snapshot();
 
2121
 
 
2122
  if(!do_apply_solution_enabled_from_state(state))
 
2123
    {
 
2124
      beep();
 
2125
      return;
 
2126
    }
 
2127
  else
 
2128
    {
 
2129
      undo_group *undo=new apt_undo_group;
 
2130
      try
 
2131
        {
 
2132
          (*apt_cache_file)->apply_solution(resman->get_solution(state.selected_solution, aptcfg->FindI(PACKAGE "::ProblemResolver::StepLimit", 5000)),
 
2133
                                            undo);
 
2134
        }
 
2135
      catch(NoMoreSolutions)
 
2136
        {
 
2137
          show_message(_("Unable to find a solution to apply."),
 
2138
                       NULL,
 
2139
                       cw::get_style("Error"));
 
2140
        }
 
2141
      catch(NoMoreTime)
 
2142
        {
 
2143
          show_message(_("Ran out of time while trying to find a solution."),
 
2144
                       NULL,
 
2145
                       cw::get_style("Error"));
 
2146
        }
 
2147
 
 
2148
      if(!undo->empty())
 
2149
        {
 
2150
          apt_undos->add_item(undo);
 
2151
          package_states_changed();
 
2152
        }
 
2153
      else
 
2154
        delete undo;
 
2155
    }
 
2156
}
 
2157
 
 
2158
namespace
 
2159
{
 
2160
  bool do_reject_break_holds_enabled()
 
2161
  {
 
2162
    return resman != NULL && resman->resolver_exists();
 
2163
  }
 
2164
 
 
2165
  void do_reject_break_holds()
 
2166
  {
 
2167
    if(!do_reject_break_holds_enabled())
 
2168
      beep();
 
2169
    else
 
2170
      resman->reject_break_holds();
 
2171
  }
 
2172
}
 
2173
 
 
2174
static void do_nullify_solver(cw::widget_ref *solver)
 
2175
{
 
2176
  *solver = NULL;
 
2177
}
 
2178
 
 
2179
static bool do_examine_solution_enabled()
 
2180
{
 
2181
  return resman != NULL && resman->resolver_exists() &&
 
2182
    aptcfg->FindI(PACKAGE "::ProblemResolver::StepLimit", 5000) > 0;
 
2183
}
 
2184
 
 
2185
void do_examine_solution()
 
2186
{
 
2187
  if(!do_examine_solution_enabled())
 
2188
    {
 
2189
      beep();
 
2190
      return;
 
2191
    }
 
2192
 
 
2193
  static cw::widget_ref solver;
 
2194
 
 
2195
  if(solver.valid())
 
2196
    solver->show();
 
2197
  else
 
2198
    {
 
2199
      solver = make_solution_screen();
 
2200
      solver->destroyed.connect(sigc::bind(sigc::ptr_fun(&do_nullify_solver),
 
2201
                                           &solver));
 
2202
 
 
2203
      add_main_widget(solver, _("Resolve Dependencies"),
 
2204
                      _("Search for solutions to unsatisfied dependencies"),
 
2205
                      _("Resolve Dependencies"));
 
2206
    }
 
2207
}
 
2208
 
 
2209
static void handle_dump_resolver_response(const wstring &s)
 
2210
{
 
2211
  if(resman != NULL && resman->resolver_exists())
 
2212
    {
 
2213
      ofstream out(cw::util::transcode(s).c_str());
 
2214
 
 
2215
      if(!out)
 
2216
        _error->Errno("dump_resolver", _("Unable to open %ls"), s.c_str());
 
2217
      else
 
2218
        {
 
2219
          resman->dump(out);
 
2220
 
 
2221
          if(!out)
 
2222
            _error->Errno("dump_resolver", _("Error while dumping resolver state"));
 
2223
        }
 
2224
    }
 
2225
}
 
2226
 
 
2227
static void do_dump_resolver()
 
2228
{
 
2229
  static cw::editline::history_list history;
 
2230
 
 
2231
  if(resman != NULL && resman->resolver_exists())
 
2232
    prompt_string(_("File to which the resolver state should be dumped:"),
 
2233
                  "",
 
2234
                  cw::util::arg(sigc::ptr_fun(handle_dump_resolver_response)),
 
2235
                  NULL,
 
2236
                  NULL,
 
2237
                  &history);
 
2238
}
 
2239
 
 
2240
// NOTE ON TRANSLATIONS!
 
2241
//
 
2242
//   Implicitly translating stuff in the widget set would be ugly.  Everything
 
2243
// would need to make sure its input to the widget set was able to survive
 
2244
// translation.
 
2245
//
 
2246
//   Using N_ here and translating when we need to display the description
 
2247
// would be ugly.  Everything would need to make sure its input to the UI would
 
2248
// be able to handle translation.
 
2249
//
 
2250
//   What I do is ugly, but it doesn't force other bits of the program to handle
 
2251
// input to us with velvet gloves, or otherwise break stuff.  So these structures
 
2252
// just contain a char *.  I can modify the char *.  In particular, I can mark
 
2253
// these for translation, then walk through them and munge the pointers to point
 
2254
// to the translated version.  "EWWWWW!" I hear you say, and you're right, but
 
2255
// the alternatives are worse.
 
2256
 
 
2257
cw::menu_info actions_menu[]={
 
2258
  //{cw::menu_info::MENU_ITEM, N_("Test ^Errors"), NULL,
 
2259
  //N_("Generate an APT error for testing purposes"),
 
2260
  //SigC::bind(SigC::slot(&silly_test_error), "This is a test error item.")},
 
2261
 
 
2262
  cw::menu_info(cw::menu_info::MENU_ITEM, N_("^Install/remove packages"), "DoInstallRun",
 
2263
               N_("Perform all pending installs and removals"), sigc::ptr_fun(do_package_run), sigc::ptr_fun(can_start_download_and_install)),
 
2264
 
 
2265
  cw::menu_info(cw::menu_info::MENU_ITEM, N_("^Update package list"), "UpdatePackageList",
 
2266
               N_("Check for new versions of packages"), sigc::ptr_fun(do_update_lists), sigc::ptr_fun(can_start_download)),
 
2267
 
 
2268
  cw::menu_info::MENU_SEPARATOR,
 
2269
 
 
2270
  cw::menu_info(cw::menu_info::MENU_ITEM, N_("Mark Up^gradable"), "MarkUpgradable",
 
2271
               N_("Mark all upgradable packages which are not held for upgrade"),
 
2272
               sigc::ptr_fun(do_mark_upgradable), sigc::ptr_fun(do_mark_upgradable_enabled)),
 
2273
 
 
2274
  // FIXME: this is a bad name for the menu item.
 
2275
  cw::menu_info(cw::menu_info::MENU_ITEM, N_("^Forget new packages"), "ForgetNewPackages",
 
2276
               N_("Forget which packages are \"new\""),
 
2277
               sigc::ptr_fun(do_forget_new), sigc::ptr_fun(forget_new_enabled)),
 
2278
 
 
2279
  cw::menu_info(cw::menu_info::MENU_ITEM, N_("Canc^el pending actions"), NULL,
 
2280
               N_("Cancel all pending installations, removals, holds, and upgrades."),
 
2281
               sigc::ptr_fun(do_keep_all)),
 
2282
 
 
2283
  cw::menu_info(cw::menu_info::MENU_ITEM, N_("^Clean package cache"), NULL,
 
2284
               N_("Delete package files which were previously downloaded"),
 
2285
               sigc::ptr_fun(do_clean)),
 
2286
 
 
2287
  cw::menu_info(cw::menu_info::MENU_ITEM, N_("Clean ^obsolete files"), NULL,
 
2288
               N_("Delete package files which can no longer be downloaded"),
 
2289
               sigc::ptr_fun(do_autoclean), sigc::ptr_fun(do_autoclean_enabled)),
 
2290
 
 
2291
  cw::menu_info::MENU_SEPARATOR,
 
2292
 
 
2293
#ifdef WITH_RELOAD_CACHE
 
2294
  cw::menu_info(cw::menu_info::MENU_ITEM, N_("^Reload package cache"), NULL,
 
2295
               N_("Reload the package cache"),
 
2296
               sigc::ptr_fun(do_reload_cache)),
 
2297
#endif
 
2298
 
 
2299
  cw::menu_info(cw::menu_info::MENU_ITEM, N_("^Play Minesweeper"), NULL,
 
2300
               N_("Waste time trying to find mines"), sigc::ptr_fun(do_sweep)),
 
2301
 
 
2302
  cw::menu_info::MENU_SEPARATOR,
 
2303
 
 
2304
  cw::menu_info(cw::menu_info::MENU_ITEM, N_("^Become root"), NULL,
 
2305
               N_("Run 'su' to become root; this will restart the program, but your settings will be preserved"), sigc::bind(sigc::ptr_fun(do_su_to_root), ""), sigc::ptr_fun(su_to_root_enabled)),
 
2306
 
 
2307
  cw::menu_info(cw::menu_info::MENU_ITEM, N_("^Quit"), "QuitProgram",
 
2308
               N_("Exit the program"), sigc::ptr_fun(do_quit)),
 
2309
 
 
2310
  cw::menu_info::MENU_END
 
2311
};
 
2312
 
 
2313
cw::menu_info undo_menu[]={
 
2314
  cw::menu_info(cw::menu_info::MENU_ITEM, N_("Undo"), "Undo",
 
2315
               N_("Undo the last package operation or group of operations"),
 
2316
               sigc::hide_return(undo_undo.make_slot()),
 
2317
               undo_undo_enabled.make_slot()),
 
2318
 
 
2319
  cw::menu_info::MENU_END
 
2320
};
 
2321
 
 
2322
cw::menu_info package_menu[]={
 
2323
  cw::menu_info(cw::menu_info::MENU_ITEM, N_("^Install"), "Install",
 
2324
               N_("Flag the currently selected package for installation or upgrade"),
 
2325
               sigc::hide_return(package_install.make_slot()),
 
2326
               package_menu_enabled.make_slot()),
 
2327
  cw::menu_info(cw::menu_info::MENU_ITEM, N_("^Remove"), "Remove",
 
2328
               N_("Flag the currently selected package for removal"),
 
2329
               sigc::hide_return(package_remove.make_slot()),
 
2330
               package_menu_enabled.make_slot()),
 
2331
  cw::menu_info(cw::menu_info::MENU_ITEM, N_("^Purge"), "Purge",
 
2332
               N_("Flag the currently selected package and its configuration files for removal"),
 
2333
               sigc::hide_return(package_purge.make_slot()),
 
2334
               package_menu_enabled.make_slot()),
 
2335
  cw::menu_info(cw::menu_info::MENU_ITEM, N_("^Keep"), "Keep",
 
2336
               N_("Cancel any action on the selected package"),
 
2337
               sigc::hide_return(package_keep.make_slot()),
 
2338
               package_menu_enabled.make_slot()),
 
2339
  cw::menu_info(cw::menu_info::MENU_ITEM, N_("^Hold"), "Hold",
 
2340
               N_("Cancel any action on the selected package, and protect it from future upgrades"),
 
2341
               sigc::hide_return(package_hold.make_slot()),
 
2342
               package_menu_enabled.make_slot()),
 
2343
  cw::menu_info(cw::menu_info::MENU_ITEM, N_("Mark ^Auto"), "SetAuto",
 
2344
               N_("Mark the selected package as having been automatically installed; it will automatically be removed if no other packages depend on it"),
 
2345
               sigc::hide_return(package_mark_auto.make_slot()),
 
2346
               package_menu_enabled.make_slot()),
 
2347
  cw::menu_info(cw::menu_info::MENU_ITEM, N_("Mark ^Manual"), "ClearAuto",
 
2348
               N_("Mark the selected package as having been manually installed; it will not be removed unless you manually remove it"),
 
2349
               sigc::hide_return(package_unmark_auto.make_slot()),
 
2350
               package_menu_enabled.make_slot()),
 
2351
  cw::menu_info(cw::menu_info::MENU_ITEM, N_("^Forbid Version"), "ForbidUpgrade",
 
2352
               N_("Forbid the candidate version of the selected package from being installed; newer versions of the package will be installed as usual"),
 
2353
               sigc::hide_return(package_forbid.make_slot()),
 
2354
               package_forbid_enabled.make_slot()),
 
2355
  cw::menu_info::MENU_SEPARATOR,
 
2356
  cw::menu_info(cw::menu_info::MENU_ITEM, N_("I^nformation"), "InfoScreen",
 
2357
               N_("Display more information about the selected package"),
 
2358
               sigc::hide_return(package_information.make_slot()),
 
2359
               package_information_enabled.make_slot()),
 
2360
  cw::menu_info(cw::menu_info::MENU_ITEM, N_("C^ycle Package Information"), "DescriptionCycle",
 
2361
                N_("Cycle through the modes of the package information area: it can show the package's long description, a summary of its dependency status, or an analysis of why the package is required."),
 
2362
                sigc::hide_return(package_cycle_information.make_slot()),
 
2363
                package_cycle_information_enabled.make_slot()),
 
2364
  cw::menu_info(cw::menu_info::MENU_ITEM, N_("^Changelog"), "Changelog",
 
2365
               N_("Display the Debian changelog of the selected package"),
 
2366
               sigc::hide_return(package_changelog.make_slot()),
 
2367
               package_changelog_enabled.make_slot()),
 
2368
  cw::menu_info::MENU_END
 
2369
};
 
2370
 
 
2371
cw::menu_info resolver_menu[] = {
 
2372
  cw::menu_info(cw::menu_info::MENU_ITEM, N_("^Examine Solution"),
 
2373
               "ExamineSolution", N_("Examine the currently selected solution to the dependency problems."),
 
2374
               sigc::ptr_fun(do_examine_solution),
 
2375
               sigc::ptr_fun(do_examine_solution_enabled)),
 
2376
  cw::menu_info(cw::menu_info::MENU_ITEM, N_("Apply ^Solution"),
 
2377
               "ApplySolution", N_("Perform the actions contained in the currently selected solution."),
 
2378
               sigc::ptr_fun(do_apply_solution),
 
2379
               sigc::ptr_fun(do_apply_solution_enabled)),
 
2380
  cw::menu_info(cw::menu_info::MENU_ITEM, N_("^Next Solution"),
 
2381
               "NextSolution", N_("Select the next solution to the dependency problems."),
 
2382
               sigc::ptr_fun(do_next_solution),
 
2383
               sigc::ptr_fun(do_next_solution_enabled)),
 
2384
  cw::menu_info(cw::menu_info::MENU_ITEM, N_("^Previous Solution"),
 
2385
               "PrevSolution", N_("Select the previous solution to the dependency problems."),
 
2386
               sigc::ptr_fun(do_previous_solution),
 
2387
               sigc::ptr_fun(do_previous_solution_enabled)),
 
2388
  cw::menu_info(cw::menu_info::MENU_ITEM, N_("^First Solution"),
 
2389
               "FirstSolution", N_("Select the first solution to the dependency problems."),
 
2390
               sigc::ptr_fun(do_first_solution),
 
2391
               sigc::ptr_fun(do_first_solution_enabled)),
 
2392
  cw::menu_info(cw::menu_info::MENU_ITEM, N_("^Last Solution"),
 
2393
               "LastSolution", N_("Select the last solution to the dependency problems that has been generated so far."),
 
2394
               sigc::ptr_fun(do_last_solution),
 
2395
               sigc::ptr_fun(do_last_solution_enabled)),
 
2396
 
 
2397
  cw::menu_info::MENU_SEPARATOR,
 
2398
 
 
2399
  cw::menu_info(cw::menu_info::MENU_ITEM, N_("Toggle ^Rejected"),
 
2400
               "SolutionActionReject", N_("Toggle whether the currently selected action is rejected."),
 
2401
               sigc::hide_return(resolver_toggle_rejected.make_slot()),
 
2402
               resolver_toggle_rejected_enabled.make_slot()),
 
2403
 
 
2404
  cw::menu_info(cw::menu_info::MENU_ITEM, N_("Toggle ^Approved"),
 
2405
               "SolutionActionApprove", N_("Toggle whether the currently selected action is approved."),
 
2406
               sigc::hide_return(resolver_toggle_approved.make_slot()),
 
2407
               resolver_toggle_approved_enabled.make_slot()),
 
2408
 
 
2409
  cw::menu_info(cw::menu_info::MENU_ITEM, N_("^View Target"),
 
2410
               "InfoScreen", N_("View the package which will be affected by the selected action"),
 
2411
               sigc::hide_return(resolver_view_target.make_slot()),
 
2412
               resolver_view_target_enabled.make_slot()),
 
2413
 
 
2414
  cw::menu_info::MENU_SEPARATOR,
 
2415
 
 
2416
  cw::menu_info(cw::menu_info::MENU_ITEM, N_("Reject Breaking ^Holds"),
 
2417
                "RejectBreakHolds",
 
2418
                N_("Reject all actions that would change the state of held packages or install forbidden versions"),
 
2419
                sigc::ptr_fun(&do_reject_break_holds),
 
2420
                sigc::ptr_fun(&do_reject_break_holds_enabled)),
 
2421
 
 
2422
  cw::menu_info::MENU_END
 
2423
};
 
2424
 
 
2425
cw::menu_info search_menu[]={
 
2426
  cw::menu_info(cw::menu_info::MENU_ITEM, N_("^Find"), "Search",
 
2427
               N_("Search forwards"),
 
2428
               sigc::hide_return(find_search.make_slot()),
 
2429
               find_search_enabled.make_slot()),
 
2430
  cw::menu_info(cw::menu_info::MENU_ITEM, N_("^Find Backwards"), "SearchBack",
 
2431
               N_("Search backwards"),
 
2432
               sigc::hide_return(find_search_back.make_slot()),
 
2433
               find_search_back_enabled.make_slot()),
 
2434
  cw::menu_info(cw::menu_info::MENU_ITEM, N_("Find ^Again"), "ReSearch",
 
2435
               N_("Repeat the last search"),
 
2436
               sigc::hide_return(find_research.make_slot()),
 
2437
               find_research_enabled.make_slot()),
 
2438
  cw::menu_info(cw::menu_info::MENU_ITEM, N_("Find Again ^Backwards"), "RepeatSearchBack",
 
2439
               N_("Repeat the last search in the opposite direction"),
 
2440
               sigc::hide_return(find_repeat_search_back.make_slot()),
 
2441
               find_repeat_search_back_enabled.make_slot()),
 
2442
  cw::menu_info::MENU_SEPARATOR,
 
2443
  cw::menu_info(cw::menu_info::MENU_ITEM, N_("^Limit Display"),
 
2444
               "ChangePkgTreeLimit", N_("Apply a filter to the package list"),
 
2445
               sigc::hide_return(find_limit.make_slot()),
 
2446
               find_limit_enabled.make_slot()),
 
2447
  cw::menu_info(cw::menu_info::MENU_ITEM, N_("^Un-Limit Display"),
 
2448
               NULL, N_("Remove the filter from the package list"),
 
2449
               sigc::hide_return(find_cancel_limit.make_slot()),
 
2450
               find_cancel_limit_enabled.make_slot()),
 
2451
  cw::menu_info::MENU_SEPARATOR,
 
2452
  cw::menu_info(cw::menu_info::MENU_ITEM, N_("Find ^Broken"),
 
2453
               "SearchBroken", N_("Find the next package with unsatisfied dependencies"),
 
2454
               sigc::hide_return(find_broken.make_slot()),
 
2455
               find_broken_enabled.make_slot()),
 
2456
  cw::menu_info::MENU_END
 
2457
};
 
2458
 
 
2459
cw::menu_info options_menu[]={
 
2460
  cw::menu_info(cw::menu_info::MENU_ITEM, N_("^Preferences"), NULL,
 
2461
               N_("Change the behavior of aptitude"),
 
2462
               sigc::ptr_fun(do_show_options_tree)),
 
2463
 
 
2464
#if 0
 
2465
  cw::menu_info(cw::menu_info::MENU_ITEM, N_("^UI options"), NULL,
 
2466
               N_("Change the settings which affect the user interface"),
 
2467
               sigc::ptr_fun(do_show_ui_options_dlg)),
 
2468
 
 
2469
  cw::menu_info(cw::menu_info::MENU_ITEM, N_("^Dependency handling"), NULL,
 
2470
               N_("Change the settings which affect how package dependencies are handled"),
 
2471
               sigc::ptr_fun(do_show_dependency_options_dlg)),
 
2472
 
 
2473
  cw::menu_info(cw::menu_info::MENU_ITEM, N_("^Miscellaneous"), NULL,
 
2474
               N_("Change miscellaneous program settings"),
 
2475
               sigc::ptr_fun(do_show_misc_options_dlg)),
 
2476
#endif
 
2477
 
 
2478
  cw::menu_info::MENU_SEPARATOR,
 
2479
 
 
2480
  cw::menu_info(cw::menu_info::MENU_ITEM, N_("^Revert options"), NULL,
 
2481
               N_("Reset all settings to the system defaults"),
 
2482
               sigc::ptr_fun(do_revert_options)),
 
2483
 
 
2484
  //{cw::menu_info::MENU_ITEM, N_("^Save options"), NULL,
 
2485
  // N_("Save current settings for future sessions"), cw::util::arg(bind(sigc::ptr_fun(apt_dumpcfg)),
 
2486
  //                                                     PACKAGE)},
 
2487
 
 
2488
  cw::menu_info::MENU_END
 
2489
};
 
2490
 
 
2491
cw::menu_info views_menu_info[]={
 
2492
  cw::menu_info(cw::menu_info::MENU_ITEM, N_("^Next"), "CycleNext",
 
2493
               N_("View next display"), sigc::ptr_fun(do_view_next),
 
2494
               sigc::ptr_fun(view_next_prev_enabled)),
 
2495
 
 
2496
  cw::menu_info(cw::menu_info::MENU_ITEM, N_("^Prev"), "CyclePrev",
 
2497
               N_("View previous display"), sigc::ptr_fun(do_view_prev),
 
2498
               sigc::ptr_fun(view_next_prev_enabled)),
 
2499
 
 
2500
  cw::menu_info(cw::menu_info::MENU_ITEM, N_("^Close"), "Quit",
 
2501
               N_("Close this display"), sigc::ptr_fun(do_destroy_visible),
 
2502
               sigc::ptr_fun(any_view_visible)),
 
2503
 
 
2504
  cw::menu_info::MENU_SEPARATOR,
 
2505
 
 
2506
  cw::menu_info(cw::menu_info::MENU_ITEM, N_("New Package ^View"), NULL,
 
2507
               N_("Create a new default package view"),
 
2508
               sigc::ptr_fun(do_new_package_view_with_new_bar)),
 
2509
 
 
2510
  cw::menu_info(cw::menu_info::MENU_ITEM, N_("Audit ^Recommendations"), NULL,
 
2511
               N_("View packages which it is recommended that you install, but which are not currently installed."),
 
2512
               sigc::ptr_fun(do_new_recommendations_view)),
 
2513
 
 
2514
  cw::menu_info(cw::menu_info::MENU_ITEM, N_("New ^Flat Package List"), NULL,
 
2515
               N_("View all the packages on the system in a single uncategorized list"),
 
2516
               sigc::ptr_fun(do_new_flat_view_with_new_bar)),
 
2517
 
 
2518
  cw::menu_info(cw::menu_info::MENU_ITEM, N_("New ^Debtags Browser"),
 
2519
               NULL,
 
2520
               N_("Browse packages using Debtags data"),
 
2521
               sigc::ptr_fun(do_new_tag_view_with_new_bar)),
 
2522
 
 
2523
  cw::menu_info(cw::menu_info::MENU_ITEM, N_("New Categorical ^Browser"),
 
2524
               NULL,
 
2525
               N_("Browse packages by category"),
 
2526
               sigc::ptr_fun(do_new_hier_view_with_new_bar)),
 
2527
 
 
2528
  cw::menu_info::MENU_SEPARATOR,
 
2529
  cw::menu_info::MENU_END
 
2530
};
 
2531
 
 
2532
cw::menu_info help_menu_info[]={
 
2533
  cw::menu_info(cw::menu_info::MENU_ITEM, N_("^About"), NULL,
 
2534
               N_("View information about this program"),
 
2535
               sigc::ptr_fun(do_help_about)),
 
2536
 
 
2537
  cw::menu_info(cw::menu_info::MENU_ITEM, N_("^Help"), "Help",
 
2538
               N_("View the on-line help"), sigc::ptr_fun(do_help_help)),
 
2539
 
 
2540
  cw::menu_info(cw::menu_info::MENU_ITEM, N_("User's ^Manual"), NULL,
 
2541
               N_("View the detailed program manual"),
 
2542
               sigc::ptr_fun(do_help_readme)),
 
2543
 
 
2544
  cw::menu_info(cw::menu_info::MENU_ITEM, N_("^FAQ"), NULL,
 
2545
               N_("View a list of frequently asked questions"),
 
2546
               sigc::ptr_fun(do_help_faq)),
 
2547
 
 
2548
  cw::menu_info(cw::menu_info::MENU_ITEM, N_("^News"), NULL,
 
2549
                N_("View the important changes made in each version of " PACKAGE),
 
2550
               sigc::ptr_fun(do_help_news)),
 
2551
 
 
2552
  cw::menu_info(cw::menu_info::MENU_ITEM, N_("^License"), NULL,
 
2553
               N_("View the terms under which you may copy and distribute aptitude"),
 
2554
               sigc::ptr_fun(do_help_license)),
 
2555
 
 
2556
  cw::menu_info::MENU_END
 
2557
};
 
2558
 
 
2559
// This is responsible for converting a particular menu-info thingy.
 
2560
static void munge_menu(cw::menu_info *menu)
 
2561
{
 
2562
  while(menu->item_type!=cw::menu_info::MENU_END)
 
2563
    {
 
2564
      if(menu->item_description)
 
2565
        menu->item_description=_(menu->item_description);
 
2566
 
 
2567
      menu->item_name=_(menu->item_name);
 
2568
 
 
2569
      ++menu;
 
2570
    }
 
2571
}
 
2572
 
 
2573
static void do_show_menu_description(cw::menu_item *item, cw::label &label)
 
2574
{
 
2575
  if(item && item->get_description().size()>0)
 
2576
    {
 
2577
      label.show();
 
2578
      label.set_text(wrapbox(cw::text_fragment(item->get_description())));
 
2579
    }
 
2580
  else
 
2581
    {
 
2582
      label.hide();
 
2583
      label.set_text("");
 
2584
    }
 
2585
}
 
2586
 
 
2587
// I want discardable signal arguments!
 
2588
static void do_setup_columns()
 
2589
{
 
2590
  pkg_item::pkg_columnizer::setup_columns(true);
 
2591
}
 
2592
 
 
2593
static void load_options(string base, bool usetheme)
 
2594
{
 
2595
  load_styles(base+"::Styles", usetheme);
 
2596
  load_bindings(base+"::Keybindings", &cw::config::global_bindings, usetheme);
 
2597
  load_bindings(base+"::Keybindings::EditLine", cw::editline::bindings, usetheme);
 
2598
  load_bindings(base+"::Keybindings::Menu", cw::menu::bindings, usetheme);
 
2599
  load_bindings(base+"::Keybindings::Menubar", cw::menubar::bindings, usetheme);
 
2600
  load_bindings(base+"::Keybindings::Minesweeper", cmine::bindings, usetheme);
 
2601
  load_bindings(base+"::Keybindings::MinibufChoice", cw::statuschoice::bindings, usetheme);
 
2602
  load_bindings(base+"::Keybindings::Pager", cw::pager::bindings, usetheme);
 
2603
  load_bindings(base+"::KeyBindings::PkgNode", pkg_tree_node::bindings, usetheme);
 
2604
  load_bindings(base+"::Keybindings::PkgTree", pkg_tree::bindings, usetheme);
 
2605
  load_bindings(base+"::Keybindings::Table", cw::table::bindings, usetheme);
 
2606
  load_bindings(base+"::Keybindings::TextLayout", cw::text_layout::bindings, usetheme);
 
2607
  load_bindings(base+"::Keybindings::Tree", cw::tree::bindings, usetheme);
 
2608
}
 
2609
 
 
2610
static cw::menu_ref add_menu(cw::menu_info *info, const std::string &name,
 
2611
                            const cw::label_ref &menu_description)
 
2612
{
 
2613
  munge_menu(info);
 
2614
 
 
2615
  cw::menu_ref menu=cw::menu::create(0, 0, 0, info);
 
2616
 
 
2617
  main_menu->append_item(cw::util::transcode(name), menu);
 
2618
 
 
2619
  menu->item_highlighted.connect(sigc::bind(sigc::ptr_fun(do_show_menu_description),
 
2620
                                            menu_description.weak_ref()));
 
2621
 
 
2622
  return menu;
 
2623
}
 
2624
 
 
2625
static void do_update_show_tabs(cw::multiplex &mp)
 
2626
{
 
2627
  mp.set_show_tabs(aptcfg->FindB(PACKAGE "::UI::ViewTabs", true));
 
2628
}
 
2629
 
 
2630
// argh
 
2631
class help_bar:public cw::label
 
2632
{
 
2633
protected:
 
2634
  help_bar(const wstring &txt, const cw::style &st):cw::label(txt, st)
 
2635
  {
 
2636
    set_visibility();
 
2637
  }
 
2638
public:
 
2639
  static
 
2640
  cw::util::ref_ptr<help_bar> create(const wstring &txt, const cw::style &st)
 
2641
  {
 
2642
    cw::util::ref_ptr<help_bar> rval(new help_bar(txt, st));
 
2643
    rval->decref();
 
2644
    return rval;
 
2645
  }
 
2646
 
 
2647
  inline void set_visibility()
 
2648
  {
 
2649
    set_visible(aptcfg->FindB(PACKAGE "::UI::HelpBar", true));
 
2650
  }
 
2651
};
 
2652
typedef cw::util::ref_ptr<help_bar> help_bar_ref;
 
2653
 
 
2654
/** \brief A list of global connections that must be disconnected when the UI exits. */
 
2655
std::deque<sigc::connection> global_connections;
 
2656
 
 
2657
void ui_init()
 
2658
{
 
2659
  cw::toplevel::init();
 
2660
  init_defaults();
 
2661
 
 
2662
  // The basic behavior of the package state signal is to update the
 
2663
  // display.
 
2664
  global_connections.push_back(package_states_changed.connect(sigc::ptr_fun(cw::toplevel::update)));
 
2665
 
 
2666
  global_connections.push_back(consume_errors.connect(sigc::ptr_fun(check_apt_errors)));
 
2667
 
 
2668
  if(aptcfg->Exists(PACKAGE "::Theme"))
 
2669
    load_options(PACKAGE "::Themes::"+string(aptcfg->Find(PACKAGE "::Theme"))+"::"+PACKAGE "::UI", true);
 
2670
 
 
2671
  load_options(PACKAGE "::UI", false);
 
2672
 
 
2673
  cw::label_ref menu_description=cw::label::create("");
 
2674
 
 
2675
  main_menu=cw::menubar::create(!aptcfg->FindB(PACKAGE "::UI::Menubar-Autohide", false));
 
2676
 
 
2677
  aptcfg->connect(string(PACKAGE "::UI::Menubar-Autohide"),
 
2678
                  sigc::ptr_fun(update_menubar_autohide));
 
2679
  aptcfg->connect(string(PACKAGE "::UI::Package-Display-Format"),
 
2680
                  sigc::ptr_fun(do_setup_columns));
 
2681
 
 
2682
  global_connections.push_back(cache_closed.connect(sigc::ptr_fun(do_show_reload_message)));
 
2683
  global_connections.push_back(cache_reloaded.connect(sigc::ptr_fun(do_hide_reload_message)));
 
2684
  global_connections.push_back(cache_reloaded.connect(sigc::ptr_fun(do_connect_resolver_callback)));
 
2685
  if(apt_cache_file)
 
2686
    {
 
2687
      do_connect_resolver_callback();
 
2688
      start_solution_calculation();
 
2689
    }
 
2690
  global_connections.push_back(cache_reload_failed.connect(sigc::ptr_fun(do_hide_reload_message)));
 
2691
 
 
2692
  global_connections.push_back(cache_reloaded.connect(sigc::ptr_fun(do_connect_read_only_callbacks)));
 
2693
  if(apt_cache_file)
 
2694
    do_connect_read_only_callbacks();
 
2695
 
 
2696
  add_menu(actions_menu, _("Actions"), menu_description);
 
2697
  add_menu(undo_menu, _("Undo"), menu_description);
 
2698
  add_menu(package_menu, _("Package"), menu_description);
 
2699
  add_menu(resolver_menu, _("Resolver"), menu_description);
 
2700
  add_menu(search_menu, _("Search"), menu_description);
 
2701
  add_menu(options_menu, _("Options"), menu_description);
 
2702
  views_menu=add_menu(views_menu_info, _("Views"), menu_description);
 
2703
  add_menu(help_menu_info, _("Help"), menu_description);
 
2704
 
 
2705
  main_stacked=cw::stacked::create();
 
2706
  main_menu->set_subwidget(main_stacked);
 
2707
  main_stacked->show();
 
2708
 
 
2709
  main_stacked->connect_key_post("QuitProgram", &cw::config::global_bindings, sigc::ptr_fun(do_quit));
 
2710
  main_stacked->connect_key_post("Quit", &cw::config::global_bindings, sigc::ptr_fun(do_destroy_visible));
 
2711
  main_stacked->connect_key_post("CycleNext",
 
2712
                                 &cw::config::global_bindings,
 
2713
                                 sigc::ptr_fun(do_view_next));
 
2714
  main_stacked->connect_key_post("CyclePrev",
 
2715
                                 &cw::config::global_bindings,
 
2716
                                 sigc::ptr_fun(do_view_prev));
 
2717
  main_stacked->connect_key_post("DoInstallRun",
 
2718
                                 &cw::config::global_bindings,
 
2719
                                 sigc::ptr_fun(do_package_run));
 
2720
  main_stacked->connect_key_post("UpdatePackageList",
 
2721
                                 &cw::config::global_bindings,
 
2722
                                 sigc::ptr_fun(do_update_lists));
 
2723
  main_stacked->connect_key_post("MarkUpgradable",
 
2724
                                 &cw::config::global_bindings,
 
2725
                                 sigc::ptr_fun(do_mark_upgradable));
 
2726
  main_stacked->connect_key_post("ForgetNewPackages",
 
2727
                                 &cw::config::global_bindings,
 
2728
                                 sigc::ptr_fun(do_forget_new));
 
2729
  main_stacked->connect_key_post("Help",
 
2730
                                 &cw::config::global_bindings,
 
2731
                                 sigc::ptr_fun(do_help_help));
 
2732
  main_stacked->connect_key_post("Undo",
 
2733
                                 &cw::config::global_bindings,
 
2734
                                 sigc::hide_return(undo_undo.make_slot()));
 
2735
  main_stacked->connect_key_post("NextSolution",
 
2736
                                 &cw::config::global_bindings,
 
2737
                                 sigc::ptr_fun(do_next_solution));
 
2738
  main_stacked->connect_key_post("PrevSolution",
 
2739
                                 &cw::config::global_bindings,
 
2740
                                 sigc::ptr_fun(do_previous_solution));
 
2741
  main_stacked->connect_key_post("FirstSolution",
 
2742
                                 &cw::config::global_bindings,
 
2743
                                 sigc::ptr_fun(do_first_solution));
 
2744
  main_stacked->connect_key_post("LastSolution",
 
2745
                                 &cw::config::global_bindings,
 
2746
                                 sigc::ptr_fun(do_last_solution));
 
2747
  main_stacked->connect_key_post("ApplySolution",
 
2748
                                 &cw::config::global_bindings,
 
2749
                                 sigc::ptr_fun(do_apply_solution));
 
2750
  main_stacked->connect_key_post("ExamineSolution",
 
2751
                                 &cw::config::global_bindings,
 
2752
                                 sigc::ptr_fun(do_examine_solution));
 
2753
  main_stacked->connect_key_post("DumpResolver",
 
2754
                                 &cw::config::global_bindings,
 
2755
                                 sigc::ptr_fun(do_dump_resolver));
 
2756
 
 
2757
  main_table=cw::table::create();
 
2758
  main_stacked->add_widget(main_table);
 
2759
  main_table->show();
 
2760
 
 
2761
  // FIXME: highlight the keys.
 
2762
  wstring menu_key=cw::config::global_bindings.readable_keyname("ToggleMenuActive"),
 
2763
    help_key=cw::config::global_bindings.readable_keyname("Help"),
 
2764
    quit_key=cw::config::global_bindings.readable_keyname("Quit"),
 
2765
    update_key=cw::config::global_bindings.readable_keyname("UpdatePackageList"),
 
2766
    install_key=cw::config::global_bindings.readable_keyname("DoInstallRun");
 
2767
 
 
2768
  wstring helptext = swsprintf(W_("%ls: Menu  %ls: Help  %ls: Quit  %ls: Update  %ls: Download/Install/Remove Pkgs").c_str(),
 
2769
                        menu_key.c_str(),
 
2770
                        help_key.c_str(),
 
2771
                        quit_key.c_str(),
 
2772
                        update_key.c_str(),
 
2773
                        install_key.c_str());
 
2774
 
 
2775
  help_bar_ref help_label(help_bar::create(helptext, cw::get_style("Header")));
 
2776
  main_table->add_widget_opts(help_label, 0, 0, 1, 1,
 
2777
                              cw::table::EXPAND | cw::table::FILL | cw::table::SHRINK,
 
2778
                              cw::table::ALIGN_CENTER);
 
2779
  aptcfg->connect(string(PACKAGE "::UI::HelpBar"),
 
2780
                  sigc::mem_fun(help_label.unsafe_get_ref(), &help_bar::set_visibility));
 
2781
 
 
2782
  main_multiplex=cw::multiplex::create(aptcfg->FindB(PACKAGE "::UI::ViewTabs", true));
 
2783
  aptcfg->connect(string(PACKAGE "::UI::ViewTabs"),
 
2784
                  sigc::bind(sigc::ptr_fun(&do_update_show_tabs), main_multiplex.weak_ref()));
 
2785
  main_table->add_widget_opts(main_multiplex, 1, 0, 1, 1,
 
2786
                              cw::table::EXPAND | cw::table::FILL | cw::table::SHRINK,
 
2787
                              cw::table::EXPAND | cw::table::FILL | cw::table::SHRINK);
 
2788
  main_multiplex->show();
 
2789
 
 
2790
  cw::widget_ref b=make_broken_indicator();
 
2791
  main_table->add_widget_opts(b, 2, 0, 1, 1,
 
2792
                              cw::table::EXPAND | cw::table::FILL | cw::table::SHRINK,
 
2793
                              cw::table::ALIGN_CENTER);
 
2794
 
 
2795
  main_status_multiplex=cw::multiplex::create();
 
2796
  main_status_multiplex->set_bg_style(cw::get_style("Status"));
 
2797
  main_table->add_widget_opts(main_status_multiplex, 3, 0, 1, 1,
 
2798
                              cw::table::EXPAND | cw::table::FILL | cw::table::SHRINK,
 
2799
                              cw::table::ALIGN_CENTER);
 
2800
  main_status_multiplex->show();
 
2801
 
 
2802
  main_status_multiplex->add_widget(menu_description);
 
2803
 
 
2804
  main_menu->show();
 
2805
 
 
2806
  cw::toplevel::settoplevel(main_menu);
 
2807
 
 
2808
  check_apt_errors();
 
2809
  cw::toplevel::main_hook.connect(sigc::ptr_fun(check_apt_errors));
 
2810
 
 
2811
  help_label->set_visibility();
 
2812
 
 
2813
  update_menubar_autohide();
 
2814
 
 
2815
  // Force parsing of the column stuff.
 
2816
  // FIXME: put this in load_options() and kill all other references
 
2817
  //       to setup_columns?
 
2818
  pkg_item::pkg_columnizer::setup_columns();
 
2819
 
 
2820
  // Make sure the broken indicator doesn't annoyingly pop up for a
 
2821
  // moment. (hack?)
 
2822
  //
 
2823
  // Note that it *should* be visible if we enter this code from the
 
2824
  // command-line (i.e., with the cache already loaded).  More hack?
 
2825
  if(resman != NULL && resman->resolver_exists())
 
2826
    b->show();
 
2827
  else
 
2828
    b->hide();
 
2829
 
 
2830
  maybe_show_old_tmpdir_message();
 
2831
}
 
2832
 
 
2833
struct clear_global_connections_on_exit
 
2834
{
 
2835
  clear_global_connections_on_exit()
 
2836
  {
 
2837
  }
 
2838
 
 
2839
  ~clear_global_connections_on_exit()
 
2840
  {
 
2841
    for(std::deque<sigc::connection>::iterator it = global_connections.begin();
 
2842
        it != global_connections.end(); ++it)
 
2843
      it->disconnect();
 
2844
    global_connections.clear();
 
2845
  }
 
2846
};
 
2847
 
 
2848
void ui_main()
 
2849
{
 
2850
  clear_global_connections_on_exit clearer;
 
2851
 
 
2852
  cw::toplevel::mainloop();
 
2853
 
 
2854
  if(apt_cache_file &&
 
2855
     (aptitudeDepCache *) (*apt_cache_file) &&
 
2856
     apt_cache_file->is_locked())
 
2857
    {
 
2858
      progress_ref p=gen_progress_bar();
 
2859
      (*apt_cache_file)->save_selection_list(*p->get_progress().unsafe_get_ref());
 
2860
      p->destroy();
 
2861
    }
 
2862
 
 
2863
  cw::toplevel::shutdown();
 
2864
}
 
2865
 
 
2866
void popup_widget(const cw::widget_ref &w, bool do_show_all)
 
2867
{
 
2868
  main_stacked->add_widget(w);
 
2869
 
 
2870
  if(do_show_all)
 
2871
    w->show_all();
 
2872
  else
 
2873
    w->show();
 
2874
}
 
2875
 
 
2876
static void setup_main_widget(const cw::widget_ref &w, const std::wstring &menuref,
 
2877
                              const std::wstring &menudesc)
 
2878
{
 
2879
  cw::menu_item *menuentry=new cw::menu_item(menuref, "", menudesc);
 
2880
 
 
2881
  // FIXME: if w is removed from the multiplexer but not destroyed, this may
 
2882
  //       break.  Fix for now: Don't Do That Then!
 
2883
  w->destroyed.connect(sigc::bind(sigc::mem_fun(views_menu.unsafe_get_ref(), &cw::menu::remove_item), menuentry));
 
2884
  menuentry->selected.connect(sigc::mem_fun(w.unsafe_get_ref(), &cw::widget::show));
 
2885
 
 
2886
  views_menu->append_item(menuentry);
 
2887
}
 
2888
 
 
2889
// Handles the case where the last view is destroyed directly (other than
 
2890
// through do_destroy_visible); for instance, when a download completes.
 
2891
static void main_widget_destroyed()
 
2892
{
 
2893
  if(aptcfg->FindB(PACKAGE "::UI::Exit-On-Last-Close", true) &&
 
2894
     main_multiplex->num_children()==0)
 
2895
    // Don't prompt -- if the last view is destroyed, assume it was by
 
2896
    // the user's request.
 
2897
    file_quit();
 
2898
}
 
2899
 
 
2900
void add_main_widget(const cw::widget_ref &w, const std::wstring &menuref,
 
2901
                     const std::wstring &menudesc,
 
2902
                     const std::wstring &tabdesc)
 
2903
{
 
2904
  setup_main_widget(w, menuref, menudesc);
 
2905
  main_multiplex->add_widget(w, tabdesc);
 
2906
  w->show();
 
2907
  w->destroyed.connect(sigc::ptr_fun(main_widget_destroyed));
 
2908
 
 
2909
  update_menubar_autohide();
 
2910
}
 
2911
 
 
2912
void add_main_widget(const cw::widget_ref &w, const std::string &menuref,
 
2913
                     const std::string &menudesc,
 
2914
                     const std::string &tabdesc)
 
2915
{
 
2916
  add_main_widget(w, cw::util::transcode(menuref), cw::util::transcode(menudesc),
 
2917
                  cw::util::transcode(tabdesc));
 
2918
}
 
2919
 
 
2920
void insert_main_widget(const cw::widget_ref &w, const std::wstring &menuref,
 
2921
                        const std::wstring &menudesc,
 
2922
                        const std::wstring &tabdesc)
 
2923
{
 
2924
  setup_main_widget(w, menuref, menudesc);
 
2925
  main_multiplex->add_widget_after(w, main_multiplex->visible_widget(), tabdesc);
 
2926
  w->show();
 
2927
 
 
2928
  update_menubar_autohide();
 
2929
}
 
2930
 
 
2931
void insert_main_widget(const cw::widget_ref &w, const std::string &menuref,
 
2932
                        const std::string &menudesc,
 
2933
                        const std::string &tabdesc)
 
2934
{
 
2935
  insert_main_widget(w, cw::util::transcode(menuref),
 
2936
                     cw::util::transcode(menudesc), cw::util::transcode(tabdesc));
 
2937
}
 
2938
 
 
2939
cw::widget_ref active_main_widget()
 
2940
{
 
2941
  return main_multiplex->visible_widget();
 
2942
}
 
2943
 
 
2944
progress_ref gen_progress_bar()
 
2945
{
 
2946
  progress_ref rval = progress::create();
 
2947
 
 
2948
  main_status_multiplex->add_visible_widget(rval, true);
 
2949
 
 
2950
  return rval;
 
2951
}
 
2952
 
 
2953
cw::fragment *wrapbox(cw::fragment *contents)
 
2954
{
 
2955
  if(aptcfg->FindB(PACKAGE "::UI::Fill-Text", false))
 
2956
    return fillbox(contents);
 
2957
  else
 
2958
    return flowbox(contents);
 
2959
}
 
2960
 
 
2961
static void reset_status_download()
 
2962
{
 
2963
  active_status_download=NULL;
 
2964
}
 
2965
 
 
2966
std::pair<download_signal_log *,
 
2967
          download_list_ref>
 
2968
gen_download_progress(bool force_noninvasive,
 
2969
                      bool list_update,
 
2970
                      const wstring &title,
 
2971
                      const wstring &longtitle,
 
2972
                      const wstring &tablabel)
 
2973
{
 
2974
  download_signal_log *m=new download_signal_log;
 
2975
  download_list_ref w=NULL;
 
2976
 
 
2977
  if(force_noninvasive ||
 
2978
     aptcfg->FindB(PACKAGE "::UI::Minibuf-Download-Bar", false))
 
2979
    {
 
2980
      w = download_list::create(false, !list_update);
 
2981
      main_status_multiplex->add_visible_widget(w, true);
 
2982
      active_status_download=w;
 
2983
      w->destroyed.connect(sigc::ptr_fun(&reset_status_download));
 
2984
    }
 
2985
  else
 
2986
    {
 
2987
      w = download_list::create(true, !list_update);
 
2988
      add_main_widget(w, title, longtitle, tablabel);
 
2989
    }
 
2990
 
 
2991
  m->MediaChange_sig.connect(sigc::mem_fun(w.unsafe_get_ref(),
 
2992
                                           &download_list::MediaChange));
 
2993
  m->IMSHit_sig.connect(sigc::mem_fun(w.unsafe_get_ref(),
 
2994
                                      &download_list::IMSHit));
 
2995
 
 
2996
  m->Fetch_sig.connect(sigc::mem_fun(w.unsafe_get_ref(),
 
2997
                                     &download_list::Fetch));
 
2998
 
 
2999
  m->Done_sig.connect(sigc::mem_fun(w.unsafe_get_ref(),
 
3000
                                    &download_list::Done));
 
3001
 
 
3002
  m->Fail_sig.connect(sigc::mem_fun(w.unsafe_get_ref(),
 
3003
                                    &download_list::Fail));
 
3004
 
 
3005
  m->Pulse_sig.connect(sigc::mem_fun(w.unsafe_get_ref(),
 
3006
                                     &download_list::Pulse));
 
3007
 
 
3008
  m->Start_sig.connect(sigc::mem_fun(w.unsafe_get_ref(),
 
3009
                                     &download_list::Start));
 
3010
 
 
3011
  m->Stop_sig.connect(sigc::mem_fun(w.unsafe_get_ref(),
 
3012
                                    &download_list::Stop));
 
3013
 
 
3014
  m->Complete_sig.connect(sigc::mem_fun(w.unsafe_get_ref(),
 
3015
                                        &download_list::Complete));
 
3016
 
 
3017
  return std::make_pair(m, w);
 
3018
}
 
3019
 
 
3020
static void do_prompt_string(const wstring &s,
 
3021
                             cw::editline &e,
 
3022
                             sigc::slot0<void> realslot)
 
3023
{
 
3024
  e.add_to_history(s);
 
3025
  realslot();
 
3026
}
 
3027
 
 
3028
std::pair<download_signal_log *, download_list_ref>
 
3029
gen_download_progress(bool force_noninvasive,
 
3030
                      bool list_update,
 
3031
                      const string &title,
 
3032
                      const string &longtitle,
 
3033
                      const string &tablabel)
 
3034
{
 
3035
  return gen_download_progress(force_noninvasive,
 
3036
                               list_update,
 
3037
                               cw::util::transcode(title),
 
3038
                               cw::util::transcode(longtitle),
 
3039
                               cw::util::transcode(tablabel));
 
3040
}
 
3041
 
 
3042
void prompt_string(const std::wstring &prompt,
 
3043
                   const std::wstring &text,
 
3044
                   cw::util::slotarg<sigc::slot1<void, wstring> > slot,
 
3045
                   cw::util::slotarg<sigc::slot0<void> > cancel_slot,
 
3046
                   cw::util::slotarg<sigc::slot1<void, wstring> > changed_slot,
 
3047
                   cw::editline::history_list *history)
 
3048
{
 
3049
  if(aptcfg->FindB(PACKAGE "::UI::Minibuf-Prompts"))
 
3050
    {
 
3051
      cw::editline_ref e=cw::editline::create(prompt, text, history);
 
3052
      e->set_allow_wrap(true);
 
3053
      e->set_clear_on_first_edit(true);
 
3054
      if(slot)
 
3055
        e->entered.connect(*slot);
 
3056
 
 
3057
      e->entered.connect(sigc::bind(sigc::ptr_fun(do_prompt_string),
 
3058
                                    e.weak_ref(),
 
3059
                                    sigc::mem_fun(e.unsafe_get_ref(), &cw::widget::destroy)));
 
3060
      if(changed_slot)
 
3061
        e->text_changed.connect(*changed_slot);
 
3062
 
 
3063
      e->connect_key("Cancel",
 
3064
                     &cw::config::global_bindings,
 
3065
                     sigc::mem_fun(e.unsafe_get_ref(), &cw::widget::destroy));
 
3066
 
 
3067
      if(cancel_slot)
 
3068
        e->connect_key("Cancel",
 
3069
                       &cw::config::global_bindings,
 
3070
                       *cancel_slot);
 
3071
 
 
3072
      main_status_multiplex->add_visible_widget(e, true);
 
3073
      main_table->focus_widget(main_status_multiplex);
 
3074
    }
 
3075
  else
 
3076
    main_stacked->add_visible_widget(cw::dialogs::string(prompt, text,
 
3077
                                                      slot, cancel_slot,
 
3078
                                                      changed_slot,
 
3079
                                                      history),
 
3080
                                     true);
 
3081
}
 
3082
 
 
3083
void prompt_string(const std::string &prompt,
 
3084
                   const std::string &text,
 
3085
                   cw::util::slotarg<sigc::slot1<void, wstring> > slot,
 
3086
                   cw::util::slotarg<sigc::slot0<void> > cancel_slot,
 
3087
                   cw::util::slotarg<sigc::slot1<void, wstring> > changed_slot,
 
3088
                   cw::editline::history_list *history)
 
3089
{
 
3090
  prompt_string(cw::util::transcode(prompt), cw::util::transcode(text),
 
3091
                slot, cancel_slot, changed_slot, history);
 
3092
}
 
3093
 
 
3094
static void do_prompt_yesno(int cval,
 
3095
                            bool deflt,
 
3096
                            cw::util::slot0arg yesslot,
 
3097
                            cw::util::slot0arg noslot)
 
3098
{
 
3099
  bool rval;
 
3100
 
 
3101
  if(deflt)
 
3102
    rval=!cval;
 
3103
  else
 
3104
    rval=cval;
 
3105
 
 
3106
  if(rval)
 
3107
    {
 
3108
      if(yesslot)
 
3109
        (*yesslot)();
 
3110
    }
 
3111
  else
 
3112
    {
 
3113
      if(noslot)
 
3114
        (*noslot)();
 
3115
    }
 
3116
}
 
3117
 
 
3118
void prompt_yesno(const std::wstring &prompt,
 
3119
                  bool deflt,
 
3120
                  cw::util::slot0arg yesslot,
 
3121
                  cw::util::slot0arg noslot)
 
3122
{
 
3123
  if(aptcfg->FindB(PACKAGE "::UI::Minibuf-Prompts"))
 
3124
    {
 
3125
      string yesstring, nostring;
 
3126
 
 
3127
      yesstring+=_("yes_key")[0];
 
3128
      nostring+=_("no_key")[0];
 
3129
      string yesnostring=deflt?yesstring+nostring:nostring+yesstring;
 
3130
 
 
3131
      cw::statuschoice_ref c=cw::statuschoice::create(prompt, cw::util::transcode(yesnostring));
 
3132
      c->chosen.connect(sigc::bind(sigc::ptr_fun(&do_prompt_yesno),
 
3133
                                   deflt,
 
3134
                                   yesslot,
 
3135
                                   noslot));
 
3136
 
 
3137
      main_status_multiplex->add_visible_widget(c, true);
 
3138
      main_table->focus_widget(main_status_multiplex);
 
3139
    }
 
3140
  else
 
3141
    main_stacked->add_visible_widget(cw::dialogs::yesno(prompt,
 
3142
                                                     yesslot,
 
3143
                                                     noslot,
 
3144
                                                     deflt),
 
3145
                                     true);
 
3146
}
 
3147
 
 
3148
void prompt_yesno_popup(cw::fragment *prompt,
 
3149
                        bool deflt,
 
3150
                        cw::util::slot0arg yesslot,
 
3151
                        cw::util::slot0arg noslot)
 
3152
{
 
3153
  main_stacked->add_visible_widget(cw::dialogs::yesno(prompt,
 
3154
                                                   yesslot,
 
3155
                                                   noslot,
 
3156
                                                   false,
 
3157
                                                   deflt),
 
3158
                                   true);
 
3159
}
 
3160
 
 
3161
void prompt_yesno(const std::string &prompt,
 
3162
                  bool deflt,
 
3163
                  cw::util::slot0arg yesslot,
 
3164
                  cw::util::slot0arg noslot)
 
3165
{
 
3166
  return prompt_yesno(cw::util::transcode(prompt), deflt, yesslot, noslot);
 
3167
}
 
3168
 
 
3169
class self_destructing_layout : public cw::text_layout
 
3170
{
 
3171
protected:
 
3172
  self_destructing_layout() : cw::text_layout()
 
3173
  {
 
3174
  }
 
3175
 
 
3176
  self_destructing_layout(cw::fragment *f) : cw::text_layout(f)
 
3177
  {
 
3178
  }
 
3179
 
 
3180
public:
 
3181
  bool handle_key(const cw::config::key &k)
 
3182
  {
 
3183
    if(!cw::text_layout::focus_me() ||
 
3184
       !cw::text_layout::handle_key(k))
 
3185
      destroy();
 
3186
 
 
3187
    return true;
 
3188
  }
 
3189
 
 
3190
  /** \brief Unlike cw::text_layouts, self-destructing widgets
 
3191
   *  can always grab the focus.
 
3192
   */
 
3193
  bool focus_me()
 
3194
  {
 
3195
    return true;
 
3196
  }
 
3197
 
 
3198
  static cw::util::ref_ptr<self_destructing_layout> create()
 
3199
  {
 
3200
    cw::util::ref_ptr<self_destructing_layout> rval(new self_destructing_layout());
 
3201
    rval->decref();
 
3202
    return rval;
 
3203
  }
 
3204
 
 
3205
  static cw::util::ref_ptr<self_destructing_layout> create(cw::fragment *f)
 
3206
  {
 
3207
    cw::util::ref_ptr<self_destructing_layout> rval(new self_destructing_layout(f));
 
3208
    rval->decref();
 
3209
    return rval;
 
3210
  }
 
3211
};
 
3212
 
 
3213
void show_message(cw::fragment *msg,
 
3214
                  cw::util::slot0arg okslot,
 
3215
                  const cw::style &st)
 
3216
{
 
3217
  msg=wrapbox(msg);
 
3218
  if(aptcfg->FindB(PACKAGE "::UI::Minibuf-Prompts"))
 
3219
    {
 
3220
      cw::text_layout_ref l = self_destructing_layout::create(msg);
 
3221
      l->set_bg_style(cw::get_style("Status")+st);
 
3222
      if(okslot)
 
3223
        l->destroyed.connect(*okslot);
 
3224
 
 
3225
      main_status_multiplex->add_visible_widget(cw::transient::create(l), true);
 
3226
      main_table->focus_widget(main_status_multiplex);
 
3227
    }
 
3228
  else
 
3229
    main_stacked->add_visible_widget(cw::dialogs::ok(msg, okslot, st), true);
 
3230
}
 
3231
 
 
3232
void show_message(const std::string &msg,
 
3233
                  cw::util::slot0arg okslot,
 
3234
                  const cw::style &st)
 
3235
{
 
3236
  show_message(cw::text_fragment(msg), okslot, st);
 
3237
}
 
3238
 
 
3239
void show_message(const std::wstring &msg,
 
3240
                  cw::util::slot0arg okslot,
 
3241
                  const cw::style &st)
 
3242
{
 
3243
  show_message(cw::text_fragment(msg), okslot, st);
 
3244
}