]> cygwin.com Git - cygwin-apps/setup.git/blob - choose.cc
8deab87433325c3f89f059618e14310317ff74ec
[cygwin-apps/setup.git] / choose.cc
1 /*
2 * Copyright (c) 2000, 2001 Red Hat, Inc.
3 * Copyright (c) 2003 Robert Collins <rbtcollins@hotmail.com>
4 *
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.
9 *
10 * A copy of the GNU General Public License can be found at
11 * http://www.gnu.org/
12 *
13 * Written by DJ Delorie <dj@cygnus.com>
14 *
15 */
16
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. */
28
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>
37
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"
48
49 #include "package_db.h"
50 #include "package_meta.h"
51
52 #include "threebar.h"
53 #include "Generic.h"
54 #include "ControlAdjuster.h"
55 #include "prereq.h"
56
57 #include "UserSettings.h"
58
59 #include "Exception.h"
60
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);
67
68 extern ThreeBarProgressPage Progress;
69
70 HWND ChooserPage::ins_dialog;
71
72 /*
73 Sizing information.
74 */
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}
87 };
88
89 ChooserPage::ChooserPage () :
90 cmd_show_set (false), saved_geom (false), saw_geom_change (false),
91 timer_id (DEFAULT_TIMER_ID), activated (false)
92 {
93 sizeProcessor.AddControlInfo (ChooserControlsInfo);
94
95 const char *fg_ret =
96 UserSettings::instance().get ("chooser_window_settings");
97 if (!fg_ret)
98 return;
99
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])))
107 {
108 saved_geom = true;
109 window_placement = buf.wp;
110 }
111 }
112
113 ChooserPage::~ChooserPage ()
114 {
115 if (saw_geom_change)
116 {
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++)
122 {
123 char intbuf[33];
124 sprintf (intbuf, "%u", buf.wpi[i]);
125 toset += comma;
126 toset += intbuf;
127 comma = ",";
128 }
129 UserSettings::instance().set ("chooser_window_settings", toset);
130 }
131 }
132
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},
141 {0}
142 };
143
144 void
145 ChooserPage::createListview ()
146 {
147 SetBusy ();
148
149 listview = new ListView();
150 listview->init(GetHWND (), IDC_CHOOSE_LIST, pkg_headers);
151
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);
156
157 SendMessage (GetDlgItem (IDC_CHOOSE_VIEW), CB_SETCURSEL, (WPARAM)chooser->getViewMode(), 0);
158
159 ClearBusy ();
160 }
161
162 void
163 ChooserPage::initialUpdateState()
164 {
165 // set the initial update state
166 if (ForceCurrentOption)
167 {
168 update_mode_id = IDC_CHOOSE_SYNC;
169 }
170 else if (hasManualSelections && !UpgradeAlsoOption)
171 {
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;
175 }
176 else
177 {
178 update_mode_id = IDC_CHOOSE_BEST;
179 }
180 changeTrust(update_mode_id, AllowTestOption, true);
181
182 static int ta[] = { IDC_CHOOSE_KEEP, IDC_CHOOSE_BEST, IDC_CHOOSE_SYNC, 0 };
183 rbset (GetHWND (), ta, update_mode_id);
184 }
185
186 /* TODO: review ::overrides for possible consolidation */
187 void
188 ChooserPage::getParentRect (HWND parent, HWND child, RECT * r)
189 {
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;
202 }
203
204 void
205 ChooserPage::PlaceDialog (bool doit)
206 {
207 if (unattended_mode == unattended)
208 /* Don't jump up and down in (fully) unattended mode */;
209 else if (doit)
210 {
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
216 {
217 ShowWindow (ins_dialog, SW_MAXIMIZE);
218 window_placement.length = sizeof window_placement;
219 GetWindowPlacement (ins_dialog, &window_placement);
220 }
221 cmd_show_set = true;
222 }
223 else if (cmd_show_set)
224 {
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;
234 }
235 }
236
237 bool
238 ChooserPage::Create ()
239 {
240 return PropertyPage::Create (IDD_CHOOSE);
241 }
242
243 void
244 ChooserPage::OnInit ()
245 {
246 CheckDlgButton (GetHWND (), IDC_CHOOSE_HIDE, BST_CHECKED);
247
248 if (AllowTestOption)
249 CheckDlgButton (GetHWND (), IDC_CHOOSE_EXP, BST_CHECKED);
250
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++)
257 {
258 std::wstring mode = LoadStringW(PickView::mode_caption((PickView::views)view));
259 SendMessageW(viewlist, CB_ADDSTRING, 0, (LPARAM)mode.c_str());
260 }
261
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);
267
268 createListview ();
269
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);
277
278 /* Set focus to search edittext control. */
279 PostMessage (GetHWND (), WM_NEXTDLGCTL,
280 (WPARAM) GetDlgItem (IDC_CHOOSE_SEARCH_EDIT), TRUE);
281 }
282
283 void
284 ChooserPage::applyCommandLinePackageSelection()
285 {
286 packagedb db;
287 for (packagedb::packagecollection::iterator i = db.packages.begin ();
288 i != db.packages.end (); ++i)
289 {
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);
313 }
314 }
315
316 void
317 ChooserPage::OnActivate()
318 {
319 SetBusy();
320
321 packagedb db;
322 db.prep();
323
324 if (!activated)
325 {
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();
331
332 activated = true;
333 }
334
335 packagedb::categoriesType::iterator it = db.categories.find("All");
336 if (it == db.categories.end ())
337 listview->setEmptyText(IDS_CHOOSER_EMPTY_NO_PACKAGES);
338
339 if (source == IDC_SOURCE_DOWNLOAD)
340 listview->setEmptyText(IDS_CHOOSER_EMPTY_DOWNLOAD);
341 else
342 listview->setEmptyText(IDS_CHOOSER_EMPTY_INSTALL);
343
344 chooser->build_category_tree();
345 chooser->init_headers();
346
347 ClearBusy();
348
349 chooser->refresh();
350 PlaceDialog (true);
351 }
352
353 long
354 ChooserPage::OnUnattended()
355 {
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;
361 }
362
363 void
364 ChooserPage::logResults()
365 {
366 Log (LOG_BABBLE) << "Chooser results..." << endLog;
367 packagedb db;
368
369 for (packagedb::packagecollection::iterator i = db.packages.begin(); i != db.packages.end(); i++)
370 {
371 i->second->logSelectionStatus();
372 }
373 }
374
375 long
376 ChooserPage::OnNext ()
377 {
378 #ifdef DEBUG
379 logResults();
380 #endif
381
382 PlaceDialog (false);
383 Progress.SetActivateTask (WM_APP_PREREQ_CHECK);
384
385 return IDD_INSTATUS;
386 }
387
388 long
389 ChooserPage::OnBack ()
390 {
391 PlaceDialog (false);
392
393 if (source == IDC_SOURCE_LOCALDIR)
394 return IDD_LOCAL_DIR;
395 else
396 return IDD_SITE;
397 }
398
399 void
400 ChooserPage::keepClicked()
401 {
402 update_mode_id = IDC_CHOOSE_KEEP;
403 packagedb db;
404 db.noChanges();
405 chooser->refresh();
406 }
407
408 void
409 ChooserPage::changeTrust(int button, bool test, bool initial)
410 {
411 SetBusy ();
412
413 update_mode_id = button;
414
415 SolverSolution::updateMode mode;
416 switch (button)
417 {
418 default:
419 case IDC_CHOOSE_KEEP:
420 mode = SolverSolution::keep;
421 break;
422
423 case IDC_CHOOSE_BEST:
424 mode = SolverSolution::updateBest;
425 break;
426
427 case IDC_CHOOSE_SYNC:
428 mode = SolverSolution::updateForce;
429 break;
430 }
431
432 packagedb db;
433 SolverTasks q;
434
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();
441
442 db.defaultTrust(q, mode, test);
443
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);
447
448 chooser->refresh();
449
450 PrereqChecker::setTestPackages(test);
451 ClearBusy ();
452 }
453
454 bool
455 ChooserPage::OnMessageCmd (int id, HWND hwndctl, UINT code)
456 {
457 #if DEBUG
458 Log (LOG_BABBLE) << "ChooserPage::OnMessageCmd " << id << " " << hwndctl << " " << std::hex << code << endLog;
459 #endif
460
461 if (id == IDC_CHOOSE_SEARCH_EDIT)
462 {
463 HWND nextButton = ::GetDlgItem(::GetParent(GetHWND()), 0x3024 /* ID_WIZNEXT */);
464 char buf[16];
465
466 if ((code == EN_CHANGE) ||
467 ((code == EN_SETFOCUS) && GetWindowText(GetDlgItem(IDC_CHOOSE_SEARCH_EDIT), buf, 15)))
468 {
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
472 //
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);
478 }
479 if (code == EN_CHANGE)
480 {
481 // apply the search filter when we stop typing
482 SetTimer(GetHWND (), timer_id, SEARCH_TIMER_DELAY, (TIMERPROC) NULL);
483 }
484 else if (code == EN_KILLFOCUS)
485 {
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);
489 }
490 return true;
491 }
492 else if (code == BN_CLICKED)
493 {
494 switch (id)
495 {
496 case IDC_CHOOSE_CLEAR_SEARCH:
497 {
498 std::string value;
499 eset (GetHWND (), IDC_CHOOSE_SEARCH_EDIT, value);
500 KillTimer (GetHWND (), timer_id);
501 chooser->SetPackageFilter (value);
502 chooser->refresh ();
503 }
504 break;
505
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;
512
513 case IDC_CHOOSE_KEEP:
514 if (IsButtonChecked (id))
515 keepClicked();
516 break;
517
518 case IDC_CHOOSE_BEST:
519 if (IsButtonChecked (id))
520 changeTrust (id, IsButtonChecked(IDC_CHOOSE_EXP), false);
521 break;
522
523 case IDC_CHOOSE_SYNC:
524 if (IsButtonChecked (id))
525 changeTrust (id, IsButtonChecked(IDC_CHOOSE_EXP), false);
526 break;
527
528 case IDC_CHOOSE_EXP:
529 changeTrust(update_mode_id, IsButtonChecked (id), false);
530 break;
531
532 case IDC_CHOOSE_HIDE:
533 chooser->setObsolete (!IsButtonChecked (id));
534 break;
535 default:
536 // Wasn't recognized or handled.
537 return false;
538 }
539
540 // Was handled since we never got to default above.
541 return true;
542 }
543 else if (code == CBN_SELCHANGE)
544 {
545 if (id == IDC_CHOOSE_VIEW)
546 {
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);
552
553 return true;
554 }
555 }
556
557 // We don't care.
558 return false;
559 }
560
561 bool
562 ChooserPage::OnNotify (NMHDR *pNmHdr, LRESULT *pResult)
563 {
564 #if DEBUG
565 Log (LOG_BABBLE) << "ChooserPage::OnNotify id:" << pNmHdr->idFrom << " hwnd:" << pNmHdr->hwndFrom << " code:" << (int)pNmHdr->code << endLog;
566 #endif
567
568 // offer messages to the listview
569 if (listview->OnNotify(pNmHdr, pResult))
570 return true;
571
572 // we don't care
573 return false;
574 }
575
576 INT_PTR CALLBACK
577 ChooserPage::OnTimerMessage (UINT message, WPARAM wParam, LPARAM lparam)
578 {
579 if (wParam == timer_id)
580 {
581 std::string value (egetString (GetHWND (), IDC_CHOOSE_SEARCH_EDIT));
582
583 KillTimer (GetHWND (), timer_id);
584 chooser->SetPackageFilter (value);
585 chooser->refresh ();
586 return TRUE;
587 }
588
589 return FALSE;
590 }
This page took 0.064993 seconds and 6 git commands to generate.