2 * Copyright (c) 2000, 2001 Red Hat, Inc.
3 * Copyright (c) 2003 Robert Collins <rbtcollins@hotmail.com>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * A copy of the GNU General Public License can be found at
13 * Written by DJ Delorie <dj@cygnus.com>
17 /* The purpose of this file is to let the user choose which packages
18 to install, and which versions of the package when more than one
19 version is provided. The "trust" level serves as an indication as
20 to which version should be the default choice. At the moment, all
21 we do is compare with previously installed packages to skip any
22 that are already installed (by setting the action to ACTION_SAME).
23 While the "trust" stuff is supported, it's not really implemented
24 yet. We always prefer the "current" option. In the future, this
25 file might have a user dialog added to let the user choose to not
26 install packages, or to install packages that aren't installed by
43 #include "LogSingleton.h"
44 #include "filemanip.h"
45 #include "io_stream.h"
46 #include "propsheet.h"
49 #include "package_db.h"
50 #include "package_meta.h"
54 #include "ControlAdjuster.h"
57 #include "UserSettings.h"
59 #include "Exception.h"
61 #include "getopt++/BoolOption.h"
62 static BoolOption
UpgradeAlsoOption (false, 'g', "upgrade-also", IDS_HELPTEXT_UPGRADE_ALSO
);
63 static BoolOption
CleanOrphansOption (false, 'o', "delete-orphans", IDS_HELPTEXT_DELETE_ORPHANS
);
64 static BoolOption
ForceCurrentOption (false, 'f', "force-current", IDS_HELPTEXT_FORCE_CURRENT
);
65 static BoolOption
PruneInstallOption (false, 'Y', "prune-install", IDS_HELPTEXT_PRUNE_INSTALL
);
66 static BoolOption
AllowTestOption (false, 't', "allow-test-packages", IDS_HELPTEXT_ALLOW_TEST
);
68 extern ThreeBarProgressPage Progress
;
70 HWND
ChooserPage::ins_dialog
;
75 static ControlAdjuster::ControlInfo ChooserControlsInfo
[] = {
76 {IDC_CHOOSE_SEARCH_LABEL
, CP_LEFT
, CP_TOP
},
77 {IDC_CHOOSE_SEARCH_EDIT
, CP_LEFT
, CP_TOP
},
78 {IDC_CHOOSE_KEEP
, CP_RIGHT
, CP_TOP
},
79 {IDC_CHOOSE_BEST
, CP_RIGHT
, CP_TOP
},
80 {IDC_CHOOSE_SYNC
, CP_RIGHT
, CP_TOP
},
81 {IDC_CHOOSE_EXP
, CP_RIGHT
, CP_TOP
},
82 {IDC_CHOOSE_VIEW
, CP_LEFT
, CP_TOP
},
83 {IDC_CHOOSE_VIEWCAPTION
, CP_LEFT
, CP_TOP
},
84 {IDC_CHOOSE_LIST
, CP_STRETCH
, CP_STRETCH
},
85 {IDC_CHOOSE_HIDE
, CP_LEFT
, CP_BOTTOM
},
89 ChooserPage::ChooserPage () :
90 cmd_show_set (false), saved_geom (false), saw_geom_change (false),
91 timer_id (DEFAULT_TIMER_ID
), activated (false)
93 sizeProcessor
.AddControlInfo (ChooserControlsInfo
);
96 UserSettings::instance().get ("chooser_window_settings");
102 char *buf_copy
= strdup (fg_ret
);
103 for (char *p
= strtok (buf_copy
, ","); p
; p
= strtok (NULL
, ","))
106 if ((py
- buf
.wpi
) == (sizeof (buf
.wpi
) / sizeof (buf
.wpi
[0])))
109 window_placement
= buf
.wp
;
113 ChooserPage::~ChooserPage ()
118 buf
.wp
= window_placement
;
120 const char *comma
= "";
121 for (unsigned i
= 0; i
< (sizeof (buf
.wpi
) / sizeof (buf
.wpi
[0])); i
++)
124 sprintf (intbuf
, "%u", buf
.wpi
[i
]);
129 UserSettings::instance().set ("chooser_window_settings", toset
);
133 static ListView::Header pkg_headers
[] = {
134 {IDS_COLUMN_PACKAGE
, LVCFMT_LEFT
, ListView::ControlType::text
},
135 {IDS_COLUMN_CURRENT
, LVCFMT_LEFT
, ListView::ControlType::text
},
136 {IDS_COLUMN_NEW
, LVCFMT_LEFT
, ListView::ControlType::popup
},
137 {IDS_COLUMN_SOURCE
, LVCFMT_LEFT
, ListView::ControlType::checkbox
},
138 {IDS_COLUMN_CATEGORIES
, LVCFMT_LEFT
, ListView::ControlType::text
},
139 {IDS_COLUMN_SIZE
, LVCFMT_RIGHT
, ListView::ControlType::text
},
140 {IDS_COLUMN_DESCR
, LVCFMT_LEFT
, ListView::ControlType::text
},
145 ChooserPage::createListview ()
149 listview
= new ListView();
150 listview
->init(GetHWND (), IDC_CHOOSE_LIST
, pkg_headers
);
152 chooser
= new PickView();
153 chooser
->init(PickView::views::Category
, listview
, this);
154 chooser
->setViewMode (!is_new_install
|| UpgradeAlsoOption
|| hasManualSelections
?
155 PickView::views::PackagePending
: PickView::views::Category
);
157 SendMessage (GetDlgItem (IDC_CHOOSE_VIEW
), CB_SETCURSEL
, (WPARAM
)chooser
->getViewMode(), 0);
163 ChooserPage::initialUpdateState()
165 // set the initial update state
166 if (ForceCurrentOption
)
168 update_mode_id
= IDC_CHOOSE_SYNC
;
170 else if (hasManualSelections
&& !UpgradeAlsoOption
)
172 // if packages are added or removed on the command-line and --upgrade-also
173 // isn't used, we keep the current versions of everything else
174 update_mode_id
= IDC_CHOOSE_KEEP
;
178 update_mode_id
= IDC_CHOOSE_BEST
;
180 changeTrust(update_mode_id
, AllowTestOption
, true);
182 static int ta
[] = { IDC_CHOOSE_KEEP
, IDC_CHOOSE_BEST
, IDC_CHOOSE_SYNC
, 0 };
183 rbset (GetHWND (), ta
, update_mode_id
);
186 /* TODO: review ::overrides for possible consolidation */
188 ChooserPage::getParentRect (HWND parent
, HWND child
, RECT
* r
)
191 ::GetWindowRect (child
, r
);
194 ::ScreenToClient (parent
, &p
);
199 ::ScreenToClient (parent
, &p
);
205 ChooserPage::PlaceDialog (bool doit
)
207 if (unattended_mode
== unattended
)
208 /* Don't jump up and down in (fully) unattended mode */;
211 pre_chooser_placement
.length
= sizeof pre_chooser_placement
;
212 GetWindowPlacement (ins_dialog
, &pre_chooser_placement
);
214 SetWindowPlacement (ins_dialog
, &window_placement
);
217 ShowWindow (ins_dialog
, SW_MAXIMIZE
);
218 window_placement
.length
= sizeof window_placement
;
219 GetWindowPlacement (ins_dialog
, &window_placement
);
223 else if (cmd_show_set
)
226 wp
.length
= sizeof wp
;
227 if (GetWindowPlacement (ins_dialog
, &wp
)
228 && memcmp (&wp
, &window_placement
, sizeof (wp
)) != 0)
229 saw_geom_change
= true;
230 SetWindowPlacement (ins_dialog
, &pre_chooser_placement
);
232 window_placement
= wp
;
233 cmd_show_set
= false;
238 ChooserPage::Create ()
240 return PropertyPage::Create (IDD_CHOOSE
);
244 ChooserPage::OnInit ()
246 CheckDlgButton (GetHWND (), IDC_CHOOSE_HIDE
, BST_CHECKED
);
249 CheckDlgButton (GetHWND (), IDC_CHOOSE_EXP
, BST_CHECKED
);
251 /* Populate view dropdown list with choices */
252 HWND viewlist
= GetDlgItem (IDC_CHOOSE_VIEW
);
253 SendMessage (viewlist
, CB_RESETCONTENT
, 0, 0);
254 for (int view
= (int)PickView::views::PackageFull
;
255 view
<= (int)PickView::views::Category
;
258 std::wstring mode
= LoadStringW(PickView::mode_caption((PickView::views
)view
));
259 SendMessageW(viewlist
, CB_ADDSTRING
, 0, (LPARAM
)mode
.c_str());
262 /* Only show text appropriate to download/install mode */
263 ShowWindow(GetDlgItem (IDC_CHOOSE_INST_TEXT_DOWNLOAD
),
264 (source
== IDC_SOURCE_DOWNLOAD
) ? SW_SHOW
: SW_HIDE
);
265 ShowWindow(GetDlgItem (IDC_CHOOSE_INST_TEXT_INSTALL
),
266 (source
!= IDC_SOURCE_DOWNLOAD
) ? SW_SHOW
: SW_HIDE
);
270 AddTooltip (IDC_CHOOSE_KEEP
, IDS_TRUSTKEEP_TOOLTIP
);
271 AddTooltip (IDC_CHOOSE_BEST
, IDS_TRUSTCURR_TOOLTIP
);
272 AddTooltip (IDC_CHOOSE_SYNC
, IDS_TRUSTSYNC_TOOLTIP
);
273 AddTooltip (IDC_CHOOSE_EXP
, IDS_TRUSTEXP_TOOLTIP
);
274 AddTooltip (IDC_CHOOSE_VIEW
, IDS_VIEWBUTTON_TOOLTIP
);
275 AddTooltip (IDC_CHOOSE_HIDE
, IDS_HIDEOBS_TOOLTIP
);
276 AddTooltip (IDC_CHOOSE_SEARCH_EDIT
, IDS_SEARCH_TOOLTIP
);
278 /* Set focus to search edittext control. */
279 PostMessage (GetHWND (), WM_NEXTDLGCTL
,
280 (WPARAM
) GetDlgItem (IDC_CHOOSE_SEARCH_EDIT
), TRUE
);
284 ChooserPage::applyCommandLinePackageSelection()
287 for (packagedb::packagecollection::iterator i
= db
.packages
.begin ();
288 i
!= db
.packages
.end (); ++i
)
290 packagemeta
&pkg
= *(i
->second
);
291 packageversion wanted_version
;
292 bool wanted
= pkg
.isManuallyWanted(wanted_version
);
293 bool deleted
= pkg
.isManuallyDeleted();
294 bool base
= pkg
.categories
.find ("Base") != pkg
.categories
.end ();
295 bool orphaned
= pkg
.categories
.find ("Orphaned") != pkg
.categories
.end ();
296 bool upgrade
= wanted
|| (!pkg
.installed
&& base
);
297 bool install
= wanted
&& !deleted
&& !pkg
.installed
;
298 bool reinstall
= (wanted
|| base
) && deleted
;
299 bool uninstall
= (!(wanted
|| base
) && (deleted
|| PruneInstallOption
))
300 || (orphaned
&& CleanOrphansOption
);
302 pkg
.set_action (packagemeta::Install_action
, UpgradeAlsoOption
? packageversion () : wanted_version
, true);
304 pkg
.set_action (packagemeta::Reinstall_action
, !wanted
? pkg
.curr
: wanted_version
);
306 pkg
.set_action (packagemeta::Uninstall_action
, packageversion ());
307 else if (PruneInstallOption
)
308 pkg
.set_action (packagemeta::NoChange_action
, pkg
.curr
);
310 pkg
.set_action (packagemeta::Install_action
, !wanted
? packageversion () : wanted_version
);
312 pkg
.set_action (packagemeta::NoChange_action
, pkg
.installed
);
317 ChooserPage::OnActivate()
326 // Do things which should only happen once, but rely on packagedb being
327 // ready to use, so OnInit() is too early
329 applyCommandLinePackageSelection();
330 initialUpdateState();
335 packagedb::categoriesType::iterator it
= db
.categories
.find("All");
336 if (it
== db
.categories
.end ())
337 listview
->setEmptyText(IDS_CHOOSER_EMPTY_NO_PACKAGES
);
339 if (source
== IDC_SOURCE_DOWNLOAD
)
340 listview
->setEmptyText(IDS_CHOOSER_EMPTY_DOWNLOAD
);
342 listview
->setEmptyText(IDS_CHOOSER_EMPTY_INSTALL
);
344 chooser
->build_category_tree();
345 chooser
->init_headers();
354 ChooserPage::OnUnattended()
356 if (unattended_mode
== unattended
)
358 // Magic constant -1 (FIXME) means 'display page but stay unattended', as
359 // also used for progress bars; see proppage.cc!PropertyPage::DialogProc().
364 ChooserPage::logResults()
366 Log (LOG_BABBLE
) << "Chooser results..." << endLog
;
369 for (packagedb::packagecollection::iterator i
= db
.packages
.begin(); i
!= db
.packages
.end(); i
++)
371 i
->second
->logSelectionStatus();
376 ChooserPage::OnNext ()
383 Progress
.SetActivateTask (WM_APP_PREREQ_CHECK
);
389 ChooserPage::OnBack ()
393 if (source
== IDC_SOURCE_LOCALDIR
)
394 return IDD_LOCAL_DIR
;
400 ChooserPage::keepClicked()
402 update_mode_id
= IDC_CHOOSE_KEEP
;
409 ChooserPage::changeTrust(int button
, bool test
, bool initial
)
413 update_mode_id
= button
;
415 SolverSolution::updateMode mode
;
419 case IDC_CHOOSE_KEEP
:
420 mode
= SolverSolution::keep
;
423 case IDC_CHOOSE_BEST
:
424 mode
= SolverSolution::updateBest
;
427 case IDC_CHOOSE_SYNC
:
428 mode
= SolverSolution::updateForce
;
435 // usually we want to apply the solver to an empty task list to get the list
436 // of packages to upgrade (if any)
437 // but initially we want a task list with any package changes caused by
438 // command line options
442 db
.defaultTrust(q
, mode
, test
);
444 // configure PickView so 'test' or 'curr' version is chosen when an
445 // uninstalled package is first clicked on.
446 chooser
->defaultTrust (test
? TRUST_TEST
: TRUST_CURR
);
450 PrereqChecker::setTestPackages(test
);
455 ChooserPage::OnMessageCmd (int id
, HWND hwndctl
, UINT code
)
458 Log (LOG_BABBLE
) << "ChooserPage::OnMessageCmd " << id
<< " " << hwndctl
<< " " << std::hex
<< code
<< endLog
;
461 if (id
== IDC_CHOOSE_SEARCH_EDIT
)
463 HWND nextButton
= ::GetDlgItem(::GetParent(GetHWND()), 0x3024 /* ID_WIZNEXT */);
466 if ((code
== EN_CHANGE
) ||
467 ((code
== EN_SETFOCUS
) && GetWindowText(GetDlgItem(IDC_CHOOSE_SEARCH_EDIT
), buf
, 15)))
469 // when focus arrives at this control and it has some text in it, or
470 // when we change the text in it, change the default button to one
471 // which immediately applies the search filter
473 // (we don't do this when the focus is on this control but it's empty
474 // (the initial state of the dialog) so that enter in that state moves
475 // onto the next page)
476 SendMessage(GetHWND (), DM_SETDEFID
, (WPARAM
) IDC_CHOOSE_DO_SEARCH
, 0);
477 SendMessage(nextButton
, BM_SETSTYLE
, BS_PUSHBUTTON
, TRUE
);
479 if (code
== EN_CHANGE
)
481 // apply the search filter when we stop typing
482 SetTimer(GetHWND (), timer_id
, SEARCH_TIMER_DELAY
, (TIMERPROC
) NULL
);
484 else if (code
== EN_KILLFOCUS
)
486 // when focus leaves this control, restore the normal default button
487 SendMessage(GetHWND (), DM_SETDEFID
, (WPARAM
) 0x3024 /* ID_WIZNEXT */, 0);
488 SendMessage(nextButton
, BM_SETSTYLE
, BS_DEFPUSHBUTTON
, TRUE
);
492 else if (code
== BN_CLICKED
)
496 case IDC_CHOOSE_CLEAR_SEARCH
:
499 eset (GetHWND (), IDC_CHOOSE_SEARCH_EDIT
, value
);
500 KillTimer (GetHWND (), timer_id
);
501 chooser
->SetPackageFilter (value
);
506 case IDC_CHOOSE_DO_SEARCH
:
507 // invisible pushbutton which is the default pushbutton while typing into
508 // the search textbox, so that 'enter' causes the filter to be applied
509 // immediately, rather than activating the next page
510 SendMessage(GetHWND (), WM_TIMER
, (WPARAM
) timer_id
, 0);
513 case IDC_CHOOSE_KEEP
:
514 if (IsButtonChecked (id
))
518 case IDC_CHOOSE_BEST
:
519 if (IsButtonChecked (id
))
520 changeTrust (id
, IsButtonChecked(IDC_CHOOSE_EXP
), false);
523 case IDC_CHOOSE_SYNC
:
524 if (IsButtonChecked (id
))
525 changeTrust (id
, IsButtonChecked(IDC_CHOOSE_EXP
), false);
529 changeTrust(update_mode_id
, IsButtonChecked (id
), false);
532 case IDC_CHOOSE_HIDE
:
533 chooser
->setObsolete (!IsButtonChecked (id
));
536 // Wasn't recognized or handled.
540 // Was handled since we never got to default above.
543 else if (code
== CBN_SELCHANGE
)
545 if (id
== IDC_CHOOSE_VIEW
)
547 // switch to the selected view
548 LRESULT view_mode
= SendMessage (GetDlgItem (IDC_CHOOSE_VIEW
),
550 if (view_mode
!= CB_ERR
)
551 chooser
->setViewMode ((PickView::views
)view_mode
);
562 ChooserPage::OnNotify (NMHDR
*pNmHdr
, LRESULT
*pResult
)
565 Log (LOG_BABBLE
) << "ChooserPage::OnNotify id:" << pNmHdr
->idFrom
<< " hwnd:" << pNmHdr
->hwndFrom
<< " code:" << (int)pNmHdr
->code
<< endLog
;
568 // offer messages to the listview
569 if (listview
->OnNotify(pNmHdr
, pResult
))
577 ChooserPage::OnTimerMessage (UINT message
, WPARAM wParam
, LPARAM lparam
)
579 if (wParam
== timer_id
)
581 std::string
value (egetString (GetHWND (), IDC_CHOOSE_SEARCH_EDIT
));
583 KillTimer (GetHWND (), timer_id
);
584 chooser
->SetPackageFilter (value
);