Add ProgramData to the env var whitelist for running scripts
[cygwin-setup.git] / choose.cc
blob8deab87433325c3f89f059618e14310317ff74ec
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", 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;
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 {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},
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;
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;
176 else
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 */
187 void
188 ChooserPage::getParentRect (HWND parent, HWND child, RECT * r)
190 POINT p;
191 ::GetWindowRect (child, r);
192 p.x = r->left;
193 p.y = r->top;
194 ::ScreenToClient (parent, &p);
195 r->left = p.x;
196 r->top = p.y;
197 p.x = r->right;
198 p.y = r->bottom;
199 ::ScreenToClient (parent, &p);
200 r->right = p.x;
201 r->bottom = p.y;
204 void
205 ChooserPage::PlaceDialog (bool doit)
207 if (unattended_mode == unattended)
208 /* Don't jump up and down in (fully) unattended mode */;
209 else if (doit)
211 pre_chooser_placement.length = sizeof pre_chooser_placement;
212 GetWindowPlacement (ins_dialog, &pre_chooser_placement);
213 if (saved_geom)
214 SetWindowPlacement (ins_dialog, &window_placement);
215 else
217 ShowWindow (ins_dialog, SW_MAXIMIZE);
218 window_placement.length = sizeof window_placement;
219 GetWindowPlacement (ins_dialog, &window_placement);
221 cmd_show_set = true;
223 else if (cmd_show_set)
225 WINDOWPLACEMENT wp;
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);
231 if (saw_geom_change)
232 window_placement = wp;
233 cmd_show_set = false;
237 bool
238 ChooserPage::Create ()
240 return PropertyPage::Create (IDD_CHOOSE);
243 void
244 ChooserPage::OnInit ()
246 CheckDlgButton (GetHWND (), IDC_CHOOSE_HIDE, BST_CHECKED);
248 if (AllowTestOption)
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;
256 view++)
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);
268 createListview ();
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);
283 void
284 ChooserPage::applyCommandLinePackageSelection()
286 packagedb db;
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);
301 if (install)
302 pkg.set_action (packagemeta::Install_action, UpgradeAlsoOption ? packageversion () : wanted_version, true);
303 else if (reinstall)
304 pkg.set_action (packagemeta::Reinstall_action, !wanted ? pkg.curr : wanted_version);
305 else if (uninstall)
306 pkg.set_action (packagemeta::Uninstall_action, packageversion ());
307 else if (PruneInstallOption)
308 pkg.set_action (packagemeta::NoChange_action, pkg.curr);
309 else if (upgrade)
310 pkg.set_action (packagemeta::Install_action, !wanted ? packageversion () : wanted_version);
311 else
312 pkg.set_action (packagemeta::NoChange_action, pkg.installed);
316 void
317 ChooserPage::OnActivate()
319 SetBusy();
321 packagedb db;
322 db.prep();
324 if (!activated)
326 // Do things which should only happen once, but rely on packagedb being
327 // ready to use, so OnInit() is too early
328 db.noChanges();
329 applyCommandLinePackageSelection();
330 initialUpdateState();
332 activated = true;
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);
341 else
342 listview->setEmptyText(IDS_CHOOSER_EMPTY_INSTALL);
344 chooser->build_category_tree();
345 chooser->init_headers();
347 ClearBusy();
349 chooser->refresh();
350 PlaceDialog (true);
353 long
354 ChooserPage::OnUnattended()
356 if (unattended_mode == unattended)
357 return OnNext ();
358 // Magic constant -1 (FIXME) means 'display page but stay unattended', as
359 // also used for progress bars; see proppage.cc!PropertyPage::DialogProc().
360 return -1;
363 void
364 ChooserPage::logResults()
366 Log (LOG_BABBLE) << "Chooser results..." << endLog;
367 packagedb db;
369 for (packagedb::packagecollection::iterator i = db.packages.begin(); i != db.packages.end(); i++)
371 i->second->logSelectionStatus();
375 long
376 ChooserPage::OnNext ()
378 #ifdef DEBUG
379 logResults();
380 #endif
382 PlaceDialog (false);
383 Progress.SetActivateTask (WM_APP_PREREQ_CHECK);
385 return IDD_INSTATUS;
388 long
389 ChooserPage::OnBack ()
391 PlaceDialog (false);
393 if (source == IDC_SOURCE_LOCALDIR)
394 return IDD_LOCAL_DIR;
395 else
396 return IDD_SITE;
399 void
400 ChooserPage::keepClicked()
402 update_mode_id = IDC_CHOOSE_KEEP;
403 packagedb db;
404 db.noChanges();
405 chooser->refresh();
408 void
409 ChooserPage::changeTrust(int button, bool test, bool initial)
411 SetBusy ();
413 update_mode_id = button;
415 SolverSolution::updateMode mode;
416 switch (button)
418 default:
419 case IDC_CHOOSE_KEEP:
420 mode = SolverSolution::keep;
421 break;
423 case IDC_CHOOSE_BEST:
424 mode = SolverSolution::updateBest;
425 break;
427 case IDC_CHOOSE_SYNC:
428 mode = SolverSolution::updateForce;
429 break;
432 packagedb db;
433 SolverTasks q;
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
439 if (initial)
440 q.setTasks();
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);
448 chooser->refresh();
450 PrereqChecker::setTestPackages(test);
451 ClearBusy ();
454 bool
455 ChooserPage::OnMessageCmd (int id, HWND hwndctl, UINT code)
457 #if DEBUG
458 Log (LOG_BABBLE) << "ChooserPage::OnMessageCmd " << id << " " << hwndctl << " " << std::hex << code << endLog;
459 #endif
461 if (id == IDC_CHOOSE_SEARCH_EDIT)
463 HWND nextButton = ::GetDlgItem(::GetParent(GetHWND()), 0x3024 /* ID_WIZNEXT */);
464 char buf[16];
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);
490 return true;
492 else if (code == BN_CLICKED)
494 switch (id)
496 case IDC_CHOOSE_CLEAR_SEARCH:
498 std::string value;
499 eset (GetHWND (), IDC_CHOOSE_SEARCH_EDIT, value);
500 KillTimer (GetHWND (), timer_id);
501 chooser->SetPackageFilter (value);
502 chooser->refresh ();
504 break;
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);
511 break;
513 case IDC_CHOOSE_KEEP:
514 if (IsButtonChecked (id))
515 keepClicked();
516 break;
518 case IDC_CHOOSE_BEST:
519 if (IsButtonChecked (id))
520 changeTrust (id, IsButtonChecked(IDC_CHOOSE_EXP), false);
521 break;
523 case IDC_CHOOSE_SYNC:
524 if (IsButtonChecked (id))
525 changeTrust (id, IsButtonChecked(IDC_CHOOSE_EXP), false);
526 break;
528 case IDC_CHOOSE_EXP:
529 changeTrust(update_mode_id, IsButtonChecked (id), false);
530 break;
532 case IDC_CHOOSE_HIDE:
533 chooser->setObsolete (!IsButtonChecked (id));
534 break;
535 default:
536 // Wasn't recognized or handled.
537 return false;
540 // Was handled since we never got to default above.
541 return true;
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),
549 CB_GETCURSEL, 0, 0);
550 if (view_mode != CB_ERR)
551 chooser->setViewMode ((PickView::views)view_mode);
553 return true;
557 // We don't care.
558 return false;
561 bool
562 ChooserPage::OnNotify (NMHDR *pNmHdr, LRESULT *pResult)
564 #if DEBUG
565 Log (LOG_BABBLE) << "ChooserPage::OnNotify id:" << pNmHdr->idFrom << " hwnd:" << pNmHdr->hwndFrom << " code:" << (int)pNmHdr->code << endLog;
566 #endif
568 // offer messages to the listview
569 if (listview->OnNotify(pNmHdr, pResult))
570 return true;
572 // we don't care
573 return false;
576 INT_PTR CALLBACK
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);
585 chooser->refresh ();
586 return TRUE;
589 return FALSE;