Add support for creating WSL symlinks
[cygwin-setup.git] / choose.cc
blob51d7197eed2ec96d0fed39bf075240e43de657d0
1 /*
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
11 * http://www.gnu.org/
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
27 default. */
29 #include "win32.h"
30 #include <commctrl.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <io.h>
34 #include <ctype.h>
35 #include <process.h>
36 #include <algorithm>
38 #include "ini.h"
39 #include "dialog.h"
40 #include "resource.h"
41 #include "state.h"
42 #include "msg.h"
43 #include "LogSingleton.h"
44 #include "filemanip.h"
45 #include "io_stream.h"
46 #include "propsheet.h"
47 #include "choose.h"
49 #include "package_db.h"
50 #include "package_meta.h"
52 #include "threebar.h"
53 #include "Generic.h"
54 #include "ControlAdjuster.h"
55 #include "prereq.h"
57 #include "UserSettings.h"
59 #include "Exception.h"
61 #include "getopt++/BoolOption.h"
62 static BoolOption UpgradeAlsoOption (false, 'g', "upgrade-also", "Also upgrade installed packages");
63 static BoolOption CleanOrphansOption (false, 'o', "delete-orphans", "Remove orphaned packages");
64 static BoolOption ForceCurrentOption (false, 'f', "force-current", "Select the current version for all packages");
65 static BoolOption PruneInstallOption (false, 'Y', "prune-install", "Prune the installation to only the requested packages");
66 static BoolOption AllowTestOption (false, 't', "allow-test-packages", "Consider package versions marked test");
68 extern ThreeBarProgressPage Progress;
70 HWND ChooserPage::ins_dialog;
73 Sizing information.
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},
86 {0, CP_LEFT, CP_TOP}
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);
95 const char *fg_ret =
96 UserSettings::instance().get ("chooser_window_settings");
97 if (!fg_ret)
98 return;
100 writer buf;
101 UINT *py = buf.wpi;
102 char *buf_copy = strdup (fg_ret);
103 for (char *p = strtok (buf_copy, ","); p; p = strtok (NULL, ","))
104 *py++ = atoi (p);
105 free (buf_copy);
106 if ((py - buf.wpi) == (sizeof (buf.wpi) / sizeof (buf.wpi[0])))
108 saved_geom = true;
109 window_placement = buf.wp;
113 ChooserPage::~ChooserPage ()
115 if (saw_geom_change)
117 writer buf;
118 buf.wp = window_placement;
119 std::string toset;
120 const char *comma = "";
121 for (unsigned i = 0; i < (sizeof (buf.wpi) / sizeof (buf.wpi[0])); i++)
123 char intbuf[33];
124 sprintf (intbuf, "%u", buf.wpi[i]);
125 toset += comma;
126 toset += intbuf;
127 comma = ",";
129 UserSettings::instance().set ("chooser_window_settings", toset);
133 static ListView::Header pkg_headers[] = {
134 {"Package", LVCFMT_LEFT, ListView::ControlType::text},
135 {"Current", LVCFMT_LEFT, ListView::ControlType::text},
136 {"New", LVCFMT_LEFT, ListView::ControlType::popup},
137 {"Src?", LVCFMT_LEFT, ListView::ControlType::checkbox},
138 {"Categories", LVCFMT_LEFT, ListView::ControlType::text},
139 {"Size", LVCFMT_RIGHT, ListView::ControlType::text},
140 {"Description", LVCFMT_LEFT, ListView::ControlType::text},
144 void
145 ChooserPage::createListview ()
147 SetBusy ();
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);
159 ClearBusy ();
162 void
163 ChooserPage::initialUpdateState()
165 // set the initial update state
166 if (ForceCurrentOption)
168 update_mode_id = IDC_CHOOSE_SYNC;
169 changeTrust(update_mode_id, AllowTestOption, true);
171 else if (hasManualSelections && !UpgradeAlsoOption)
173 // if packages are added or removed on the command-line and --upgrade-also
174 // isn't used, we keep the current versions of everything else
175 update_mode_id = IDC_CHOOSE_KEEP;
177 else
179 update_mode_id = IDC_CHOOSE_BEST;
180 changeTrust (update_mode_id, AllowTestOption, true);
183 static int ta[] = { IDC_CHOOSE_KEEP, IDC_CHOOSE_BEST, IDC_CHOOSE_SYNC, 0 };
184 rbset (GetHWND (), ta, update_mode_id);
187 /* TODO: review ::overrides for possible consolidation */
188 void
189 ChooserPage::getParentRect (HWND parent, HWND child, RECT * r)
191 POINT p;
192 ::GetWindowRect (child, r);
193 p.x = r->left;
194 p.y = r->top;
195 ::ScreenToClient (parent, &p);
196 r->left = p.x;
197 r->top = p.y;
198 p.x = r->right;
199 p.y = r->bottom;
200 ::ScreenToClient (parent, &p);
201 r->right = p.x;
202 r->bottom = p.y;
205 void
206 ChooserPage::PlaceDialog (bool doit)
208 if (unattended_mode == unattended)
209 /* Don't jump up and down in (fully) unattended mode */;
210 else if (doit)
212 pre_chooser_placement.length = sizeof pre_chooser_placement;
213 GetWindowPlacement (ins_dialog, &pre_chooser_placement);
214 if (saved_geom)
215 SetWindowPlacement (ins_dialog, &window_placement);
216 else
218 ShowWindow (ins_dialog, SW_MAXIMIZE);
219 window_placement.length = sizeof window_placement;
220 GetWindowPlacement (ins_dialog, &window_placement);
222 cmd_show_set = true;
224 else if (cmd_show_set)
226 WINDOWPLACEMENT wp;
227 wp.length = sizeof wp;
228 if (GetWindowPlacement (ins_dialog, &wp)
229 && memcmp (&wp, &window_placement, sizeof (wp)) != 0)
230 saw_geom_change = true;
231 SetWindowPlacement (ins_dialog, &pre_chooser_placement);
232 if (saw_geom_change)
233 window_placement = wp;
234 cmd_show_set = false;
238 bool
239 ChooserPage::Create ()
241 return PropertyPage::Create (IDD_CHOOSE);
244 void
245 ChooserPage::setPrompt(char const *aString)
247 ::SetWindowText (GetDlgItem (IDC_CHOOSE_INST_TEXT), aString);
250 void
251 ChooserPage::OnInit ()
253 CheckDlgButton (GetHWND (), IDC_CHOOSE_HIDE, BST_CHECKED);
255 if (AllowTestOption)
256 CheckDlgButton (GetHWND (), IDC_CHOOSE_EXP, BST_CHECKED);
258 /* Populate view dropdown list with choices */
259 HWND viewlist = GetDlgItem (IDC_CHOOSE_VIEW);
260 SendMessage (viewlist, CB_RESETCONTENT, 0, 0);
261 for (int view = (int)PickView::views::PackageFull;
262 view <= (int)PickView::views::Category;
263 view++)
265 SendMessage(viewlist, CB_ADDSTRING, 0, (LPARAM)PickView::mode_caption((PickView::views)view));
268 if (source == IDC_SOURCE_DOWNLOAD)
269 setPrompt("Select packages to download ");
270 else
271 setPrompt("Select packages to install ");
273 createListview ();
275 AddTooltip (IDC_CHOOSE_KEEP, IDS_TRUSTKEEP_TOOLTIP);
276 AddTooltip (IDC_CHOOSE_BEST, IDS_TRUSTCURR_TOOLTIP);
277 AddTooltip (IDC_CHOOSE_SYNC, IDS_TRUSTSYNC_TOOLTIP);
278 AddTooltip (IDC_CHOOSE_EXP, IDS_TRUSTEXP_TOOLTIP);
279 AddTooltip (IDC_CHOOSE_VIEW, IDS_VIEWBUTTON_TOOLTIP);
280 AddTooltip (IDC_CHOOSE_HIDE, IDS_HIDEOBS_TOOLTIP);
281 AddTooltip (IDC_CHOOSE_SEARCH_EDIT, IDS_SEARCH_TOOLTIP);
283 /* Set focus to search edittext control. */
284 PostMessage (GetHWND (), WM_NEXTDLGCTL,
285 (WPARAM) GetDlgItem (IDC_CHOOSE_SEARCH_EDIT), TRUE);
288 void
289 ChooserPage::applyCommandLinePackageSelection()
291 packagedb db;
292 for (packagedb::packagecollection::iterator i = db.packages.begin ();
293 i != db.packages.end (); ++i)
295 packagemeta &pkg = *(i->second);
296 packageversion wanted_version;
297 bool wanted = pkg.isManuallyWanted(wanted_version);
298 bool deleted = pkg.isManuallyDeleted();
299 bool base = pkg.categories.find ("Base") != pkg.categories.end ();
300 bool orphaned = pkg.categories.find ("Orphaned") != pkg.categories.end ();
301 bool upgrade = wanted || (!pkg.installed && base);
302 bool install = wanted && !deleted && !pkg.installed;
303 bool reinstall = (wanted || base) && deleted;
304 bool uninstall = (!(wanted || base) && (deleted || PruneInstallOption))
305 || (orphaned && CleanOrphansOption);
306 if (install)
307 pkg.set_action (packagemeta::Install_action, UpgradeAlsoOption ? packageversion () : wanted_version, true);
308 else if (reinstall)
309 pkg.set_action (packagemeta::Reinstall_action, !wanted ? pkg.curr : wanted_version);
310 else if (uninstall)
311 pkg.set_action (packagemeta::Uninstall_action, packageversion ());
312 else if (PruneInstallOption)
313 pkg.set_action (packagemeta::NoChange_action, pkg.curr);
314 else if (upgrade)
315 pkg.set_action (packagemeta::Install_action, !wanted ? packageversion () : wanted_version);
316 else
317 pkg.set_action (packagemeta::NoChange_action, pkg.installed);
321 void
322 ChooserPage::OnActivate()
324 SetBusy();
326 packagedb db;
327 db.prep();
329 if (!activated)
331 // Do things which should only happen once, but rely on packagedb being
332 // ready to use, so OnInit() is too early
333 db.noChanges();
334 applyCommandLinePackageSelection();
335 initialUpdateState();
337 activated = true;
340 packagedb::categoriesType::iterator it = db.categories.find("All");
341 if (it == db.categories.end ())
342 listview->setEmptyText("No packages found.");
343 if (source == IDC_SOURCE_DOWNLOAD)
344 listview->setEmptyText("Nothing to download.");
345 else
346 listview->setEmptyText("Nothing to install or update.");
348 chooser->build_category_tree();
349 chooser->init_headers();
351 ClearBusy();
353 chooser->refresh();
354 PlaceDialog (true);
357 long
358 ChooserPage::OnUnattended()
360 if (unattended_mode == unattended)
361 return OnNext ();
362 // Magic constant -1 (FIXME) means 'display page but stay unattended', as
363 // also used for progress bars; see proppage.cc!PropertyPage::DialogProc().
364 return -1;
367 void
368 ChooserPage::logResults()
370 Log (LOG_BABBLE) << "Chooser results..." << endLog;
371 packagedb db;
373 for (packagedb::packagecollection::iterator i = db.packages.begin(); i != db.packages.end(); i++)
375 i->second->logSelectionStatus();
379 long
380 ChooserPage::OnNext ()
382 #ifdef DEBUG
383 logResults();
384 #endif
386 PlaceDialog (false);
387 Progress.SetActivateTask (WM_APP_PREREQ_CHECK);
389 return IDD_INSTATUS;
392 long
393 ChooserPage::OnBack ()
395 PlaceDialog (false);
397 if (source == IDC_SOURCE_LOCALDIR)
398 return IDD_LOCAL_DIR;
399 else
400 return IDD_SITE;
403 void
404 ChooserPage::keepClicked()
406 update_mode_id = IDC_CHOOSE_KEEP;
407 packagedb db;
408 db.noChanges();
409 chooser->refresh();
412 void
413 ChooserPage::changeTrust(int button, bool test, bool initial)
415 SetBusy ();
417 update_mode_id = button;
419 SolverSolution::updateMode mode;
420 switch (button)
422 default:
423 case IDC_CHOOSE_KEEP:
424 mode = SolverSolution::keep;
425 break;
427 case IDC_CHOOSE_BEST:
428 mode = SolverSolution::updateBest;
429 break;
431 case IDC_CHOOSE_SYNC:
432 mode = SolverSolution::updateForce;
433 break;
436 packagedb db;
437 SolverTasks q;
439 // usually we want to apply the solver to an empty task list to get the list
440 // of packages to upgrade (if any)
441 // but initially we want a task list with any package changes caused by
442 // command line options
443 if (initial)
444 q.setTasks();
446 db.defaultTrust(q, mode, test);
448 // configure PickView so 'test' or 'curr' version is chosen when an
449 // uninstalled package is first clicked on.
450 chooser->defaultTrust (test ? TRUST_TEST : TRUST_CURR);
452 chooser->refresh();
454 PrereqChecker::setTestPackages(test);
455 ClearBusy ();
458 bool
459 ChooserPage::OnMessageCmd (int id, HWND hwndctl, UINT code)
461 #if DEBUG
462 Log (LOG_BABBLE) << "ChooserPage::OnMessageCmd " << id << " " << hwndctl << " " << std::hex << code << endLog;
463 #endif
465 if (id == IDC_CHOOSE_SEARCH_EDIT)
467 HWND nextButton = ::GetDlgItem(::GetParent(GetHWND()), 0x3024 /* ID_WIZNEXT */);
468 char buf[16];
470 if ((code == EN_CHANGE) ||
471 ((code == EN_SETFOCUS) && GetWindowText(GetDlgItem(IDC_CHOOSE_SEARCH_EDIT), buf, 15)))
473 // when focus arrives at this control and it has some text in it, or
474 // when we change the text in it, change the default button to one
475 // which immediately applies the search filter
477 // (we don't do this when the focus is on this control but it's empty
478 // (the initial state of the dialog) so that enter in that state moves
479 // onto the next page)
480 SendMessage(GetHWND (), DM_SETDEFID, (WPARAM) IDC_CHOOSE_DO_SEARCH, 0);
481 SendMessage(nextButton, BM_SETSTYLE, BS_PUSHBUTTON, TRUE);
483 if (code == EN_CHANGE)
485 // apply the search filter when we stop typing
486 SetTimer(GetHWND (), timer_id, SEARCH_TIMER_DELAY, (TIMERPROC) NULL);
488 else if (code == EN_KILLFOCUS)
490 // when focus leaves this control, restore the normal default button
491 SendMessage(GetHWND (), DM_SETDEFID, (WPARAM) 0x3024 /* ID_WIZNEXT */, 0);
492 SendMessage(nextButton, BM_SETSTYLE, BS_DEFPUSHBUTTON, TRUE);
494 return true;
496 else if (code == BN_CLICKED)
498 switch (id)
500 case IDC_CHOOSE_CLEAR_SEARCH:
502 std::string value;
503 eset (GetHWND (), IDC_CHOOSE_SEARCH_EDIT, value);
504 KillTimer (GetHWND (), timer_id);
505 chooser->SetPackageFilter (value);
506 chooser->refresh ();
508 break;
510 case IDC_CHOOSE_DO_SEARCH:
511 // invisible pushbutton which is the default pushbutton while typing into
512 // the search textbox, so that 'enter' causes the filter to be applied
513 // immediately, rather than activating the next page
514 SendMessage(GetHWND (), WM_TIMER, (WPARAM) timer_id, 0);
515 break;
517 case IDC_CHOOSE_KEEP:
518 if (IsButtonChecked (id))
519 keepClicked();
520 break;
522 case IDC_CHOOSE_BEST:
523 if (IsButtonChecked (id))
524 changeTrust (id, IsButtonChecked(IDC_CHOOSE_EXP), false);
525 break;
527 case IDC_CHOOSE_SYNC:
528 if (IsButtonChecked (id))
529 changeTrust (id, IsButtonChecked(IDC_CHOOSE_EXP), false);
530 break;
532 case IDC_CHOOSE_EXP:
533 changeTrust(update_mode_id, IsButtonChecked (id), false);
534 break;
536 case IDC_CHOOSE_HIDE:
537 chooser->setObsolete (!IsButtonChecked (id));
538 break;
539 default:
540 // Wasn't recognized or handled.
541 return false;
544 // Was handled since we never got to default above.
545 return true;
547 else if (code == CBN_SELCHANGE)
549 if (id == IDC_CHOOSE_VIEW)
551 // switch to the selected view
552 LRESULT view_mode = SendMessage (GetDlgItem (IDC_CHOOSE_VIEW),
553 CB_GETCURSEL, 0, 0);
554 if (view_mode != CB_ERR)
555 chooser->setViewMode ((PickView::views)view_mode);
557 return true;
561 // We don't care.
562 return false;
565 bool
566 ChooserPage::OnNotify (NMHDR *pNmHdr, LRESULT *pResult)
568 #if DEBUG
569 Log (LOG_BABBLE) << "ChooserPage::OnNotify id:" << pNmHdr->idFrom << " hwnd:" << pNmHdr->hwndFrom << " code:" << (int)pNmHdr->code << endLog;
570 #endif
572 // offer messages to the listview
573 if (listview->OnNotify(pNmHdr, pResult))
574 return true;
576 // we don't care
577 return false;
580 INT_PTR CALLBACK
581 ChooserPage::OnTimerMessage (UINT message, WPARAM wParam, LPARAM lparam)
583 if (wParam == timer_id)
585 std::string value (egetString (GetHWND (), IDC_CHOOSE_SEARCH_EDIT));
587 KillTimer (GetHWND (), timer_id);
588 chooser->SetPackageFilter (value);
589 chooser->refresh ();
590 return TRUE;
593 return FALSE;