]> cygwin.com Git - cygwin-apps/setup.git/blob - choose.cc
2002-11-26 Robert Collins <rbtcollins@hotmail.com>
[cygwin-apps/setup.git] / choose.cc
1 /*
2 * Copyright (c) 2000, 2001 Red Hat, Inc.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * A copy of the GNU General Public License can be found at
10 * http://www.gnu.org/
11 *
12 * Written by DJ Delorie <dj@cygnus.com>
13 *
14 */
15
16 /* The purpose of this file is to let the user choose which packages
17 to install, and which versions of the package when more than one
18 version is provided. The "trust" level serves as an indication as
19 to which version should be the default choice. At the moment, all
20 we do is compare with previously installed packages to skip any
21 that are already installed (by setting the action to ACTION_SAME).
22 While the "trust" stuff is supported, it's not really implemented
23 yet. We always prefer the "current" option. In the future, this
24 file might have a user dialog added to let the user choose to not
25 install packages, or to install packages that aren't installed by
26 default. */
27
28 #if 0
29 static const char *cvsid =
30 "\n%%% $Id$\n";
31 #endif
32
33 #include "win32.h"
34 #include <commctrl.h>
35 #include <stdio.h>
36 #include <io.h>
37 #include <ctype.h>
38 #include <process.h>
39
40 #include "dialog.h"
41 #include "resource.h"
42 #include "state.h"
43 #include "msg.h"
44 #include "LogSingleton.h"
45 #include "filemanip.h"
46 #include "io_stream.h"
47 #include "propsheet.h"
48 #include "choose.h"
49 #include "category.h"
50
51 #include "package_db.h"
52 #include "package_meta.h"
53 #include "package_version.h"
54
55 #include "PickView.h"
56
57 #include "port.h"
58 #include "threebar.h"
59
60 #include "download.h"
61
62 using namespace std;
63
64 extern ThreeBarProgressPage Progress;
65
66 static int initialized = 0;
67
68 static HWND lv, choose_inst_text;
69 static PickView *chooser = NULL;
70
71 static void set_view_mode (HWND h, PickView::views mode);
72
73 static void
74 paint (HWND hwnd)
75 {
76 HDC hdc;
77 PAINTSTRUCT ps;
78 int x, y;
79
80 hdc = BeginPaint (hwnd, &ps);
81
82 SelectObject (hdc, chooser->sysfont);
83 SetBkColor (hdc, GetSysColor (COLOR_WINDOW));
84 SetTextColor (hdc, GetSysColor (COLOR_WINDOWTEXT));
85
86 RECT cr;
87 GetClientRect (hwnd, &cr);
88
89 x = cr.left - chooser->scroll_ulc_x;
90 y = cr.top - chooser->scroll_ulc_y + chooser->header_height;
91
92 IntersectClipRect (hdc, cr.left, cr.top + chooser->header_height, cr.right,
93 cr.bottom);
94
95 chooser->contents.paint (hdc, x, y, 0, (chooser->get_view_mode () ==
96 PickView::views::Category) ? 0 : 1);
97
98 if (chooser->contents.itemcount () == 0)
99 {
100 static const char *msg = "Nothing to Install/Update";
101 if (source == IDC_SOURCE_DOWNLOAD)
102 msg = "Nothing to Download";
103 TextOut (hdc, HMARGIN, chooser->header_height, msg, strlen (msg));
104 }
105
106 EndPaint (hwnd, &ps);
107 }
108
109 static LRESULT CALLBACK
110 list_vscroll (HWND hwnd, HWND hctl, UINT code, int pos)
111 {
112 chooser->scroll (hwnd, SB_VERT, &chooser->scroll_ulc_y, code);
113 return 0;
114 }
115
116 static LRESULT CALLBACK
117 list_hscroll (HWND hwnd, HWND hctl, UINT code, int pos)
118 {
119 chooser->scroll (hwnd, SB_HORZ, &chooser->scroll_ulc_x, code);
120 return 0;
121 }
122
123 static LRESULT CALLBACK
124 list_click (HWND hwnd, BOOL dblclk, int x, int y, UINT hitCode)
125 {
126 int row, refresh;
127
128 if (chooser->contents.itemcount () == 0)
129 return 0;
130
131 if (y < chooser->header_height)
132 return 0;
133 x += chooser->scroll_ulc_x;
134 y += chooser->scroll_ulc_y - chooser->header_height;
135
136 row = (y + ROW_MARGIN / 2) / chooser->row_height;
137
138 if (row < 0 || row >= chooser->contents.itemcount ())
139 return 0;
140
141 refresh = chooser->click (row, x);
142
143 // XXX we need a method to queryt he database to see if more
144 // than just one package has changed! Until then...
145 #if 0
146 if (refresh)
147 {
148 #endif
149 RECT r;
150 GetClientRect (lv, &r);
151 SCROLLINFO si;
152 memset (&si, 0, sizeof (si));
153 si.cbSize = sizeof (si);
154 si.fMask = SIF_ALL; /* SIF_RANGE was giving strange behaviour */
155 si.nMin = 0;
156
157 si.nMax = chooser->contents.itemcount () * chooser->row_height;
158 si.nPage = r.bottom - chooser->header_height;
159
160 /* if we are under the minimum display count ,
161 * set the offset to 0
162 */
163 if ((unsigned int) si.nMax <= si.nPage)
164 chooser->scroll_ulc_y = 0;
165 si.nPos = chooser->scroll_ulc_y;
166
167 SetScrollInfo (lv, SB_VERT, &si, TRUE);
168
169 InvalidateRect (lv, &r, TRUE);
170 #if 0
171 }
172 else
173 {
174 RECT rect;
175 rect.left =
176 chooser->headers[chooser->new_col].x - chooser->scroll_ulc_x;
177 rect.right =
178 chooser->headers[chooser->src_col + 1].x - chooser->scroll_ulc_x;
179 rect.top =
180 chooser->header_height + row * chooser->row_height -
181 chooser->scroll_ulc_y;
182 rect.bottom = rect.top + chooser->row_height;
183 InvalidateRect (hwnd, &rect, TRUE);
184 }
185 #endif
186 return 0;
187 }
188
189 static LRESULT CALLBACK
190 listview_proc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
191 {
192 switch (message)
193 {
194 case WM_HSCROLL:
195 return HANDLE_WM_HSCROLL (hwnd, wParam, lParam, list_hscroll);
196 case WM_VSCROLL:
197 return HANDLE_WM_VSCROLL (hwnd, wParam, lParam, list_vscroll);
198 case WM_LBUTTONDOWN:
199 return HANDLE_WM_LBUTTONDOWN (hwnd, wParam, lParam, list_click);
200 case WM_PAINT:
201 paint (hwnd);
202 return 0;
203 case WM_NOTIFY:
204 {
205 // pnmh = (LPNMHDR) lParam
206 LPNMHEADER phdr = (LPNMHEADER) lParam;
207 switch (phdr->hdr.code)
208 {
209 case HDN_ITEMCHANGED:
210 if (phdr->hdr.hwndFrom == chooser->ListHeader ())
211 {
212 if (phdr->pitem && phdr->pitem->mask & HDI_WIDTH)
213 chooser->headers[phdr->iItem].width = phdr->pitem->cxy;
214 for (int i = 1; i <= chooser->last_col; i++)
215 chooser->headers[i].x =
216 chooser->headers[i - 1].x + chooser->headers[i - 1].width;
217 RECT r;
218 GetClientRect (hwnd, &r);
219 SCROLLINFO si;
220 si.cbSize = sizeof (si);
221 si.fMask = SIF_ALL;
222 GetScrollInfo (hwnd, SB_HORZ, &si);
223 int oldMax = si.nMax;
224 si.nMax =
225 chooser->headers[chooser->last_col].x +
226 chooser->headers[chooser->last_col].width;
227 if (si.nTrackPos && oldMax > si.nMax)
228 si.nTrackPos += si.nMax - oldMax;
229 si.nPage = r.right;
230 SetScrollInfo (hwnd, SB_HORZ, &si, TRUE);
231 InvalidateRect (hwnd, &r, TRUE);
232 if (si.nTrackPos && oldMax > si.nMax)
233 chooser->scroll (hwnd, SB_HORZ, &chooser->scroll_ulc_x,
234 SB_THUMBTRACK);
235 }
236 break;
237 default:
238 break;
239 }
240 }
241 default:
242 return DefWindowProc (hwnd, message, wParam, lParam);
243 }
244 }
245
246 static void
247 register_windows (HINSTANCE hinst)
248 {
249 WNDCLASSEX wcex;
250 static int done = 0;
251
252 if (done)
253 return;
254 done = 1;
255
256 memset (&wcex, 0, sizeof (wcex));
257 wcex.cbSize = sizeof (WNDCLASSEX);
258 wcex.style = CS_HREDRAW | CS_VREDRAW;
259 wcex.lpfnWndProc = listview_proc;
260 wcex.hInstance = hinst;
261 wcex.hIcon = LoadIcon (0, IDI_APPLICATION);
262 wcex.hCursor = LoadCursor (0, IDC_ARROW);
263 wcex.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
264 wcex.lpszClassName = "listview";
265
266 RegisterClassEx (&wcex);
267 }
268
269 static void
270 set_existence ()
271 {
272 packagedb db;
273 /* binary packages */
274 /* Remove packages that are in the db, not installed, and have no
275 mirror info and are not cached for both binary and source packages. */
276 vector <packagemeta *>::iterator i = db.packages.begin ();
277 while (i != db.packages.end ())
278 {
279 packagemeta & pkg = **i;
280 if (!pkg.installed && !pkg.accessible() &&
281 !pkg.sourceAccessible() )
282 {
283 packagemeta *pkgm = *i;
284 delete pkgm;
285 i = db.packages.erase (i);
286 }
287 else
288 ++i;
289 }
290 #if 0
291 /* remove any source packages which are not accessible */
292 vector <packagemeta *>::iterator i = db.sourcePackages.begin();
293 while (i != db.sourcePackages.end())
294 {
295 packagemeta & pkg = **i;
296 if (!packageAccessible (pkg))
297 {
298 packagemeta *pkgm = *i;
299 delete pkgm;
300 i = db.sourcePackages.erase (i);
301 }
302 else
303 ++i;
304 }
305 #endif
306 }
307
308 static void
309 fill_missing_category ()
310 {
311 packagedb db;
312 for (vector <packagemeta *>::iterator i = db.packages.begin ();
313 i != db.packages.end (); ++i)
314 {
315 packagemeta & pkg = **i;
316 if (!pkg.categories.size ())
317 pkg.add_category ("Misc");
318 pkg.add_category ("All");
319 }
320 }
321
322 static void
323 default_trust (HWND h, trusts trust)
324 {
325 chooser->deftrust = trust;
326 packagedb db;
327 for (vector <packagemeta *>::iterator i = db.packages.begin ();
328 i != db.packages.end (); ++i)
329 {
330 packagemeta & pkg = **i;
331 if (pkg.installed
332 || pkg.categories.find ("Base") != pkg.categories.end ()
333 || pkg.categories.find ("Misc") != pkg.categories.end ())
334 {
335 pkg.desired = pkg.trustp (trust);
336 if (pkg.desired)
337 pkg.desired.pick (pkg.desired.accessible()
338 && pkg.desired != pkg.installed);
339 }
340 else
341 pkg.desired = packageversion ();
342 }
343 RECT r;
344 GetClientRect (h, &r);
345 InvalidateRect (h, &r, TRUE);
346 // and then do the same for categories with no packages.
347 for (packagedb::categoriesType::iterator n = packagedb::categories.begin();
348 n != packagedb::categories.end(); ++n)
349 if (!n->second.size())
350 {
351 log (LOG_BABBLE) << "Removing empty category " << n->first << endLog;
352 packagedb::categories.erase (n++);
353 }
354 }
355
356 static void
357 set_view_mode (HWND h, PickView::views mode)
358 {
359 chooser->set_view_mode (mode);
360
361 chooser->clear_view ();
362 packagedb db;
363 if (chooser->get_view_mode () == PickView::views::Package)
364 {
365 for (vector <packagemeta *>::iterator i = db.packages.begin ();
366 i != db.packages.end (); ++i)
367 {
368 packagemeta & pkg = **i;
369 if ((!pkg.desired && pkg.installed)
370 || (pkg.desired && (pkg.desired.picked ()
371 || pkg.desired.sourcePackage().picked())))
372 chooser->insert_pkg (pkg);
373 }
374 }
375 else if (chooser->get_view_mode () == PickView::views::PackageFull)
376 {
377 for (vector <packagemeta *>::iterator i = db.packages.begin ();
378 i != db.packages.end (); ++i)
379 chooser->insert_pkg (**i);
380 }
381 else if (chooser->get_view_mode () == PickView::views::Category)
382 {
383 /* start collapsed. TODO: make this a chooser flag */
384 for (packagedb::categoriesType::iterator n
385 = packagedb::categories.begin();
386 n != packagedb::categories.end(); ++n)
387 chooser->insert_category (&*n, CATEGORY_COLLAPSED);
388 }
389
390 RECT r;
391 GetClientRect (h, &r);
392 SCROLLINFO si;
393 memset (&si, 0, sizeof (si));
394 si.cbSize = sizeof (si);
395 si.fMask = SIF_ALL;
396 si.nMin = 0;
397 si.nMax = chooser->headers[chooser->last_col].x + chooser->headers[chooser->last_col].width; // + HMARGIN;
398 si.nPage = r.right;
399 SetScrollInfo (h, SB_HORZ, &si, TRUE);
400
401 si.nMax = chooser->contents.itemcount () * chooser->row_height;
402 si.nPage = r.bottom - chooser->header_height;
403 SetScrollInfo (h, SB_VERT, &si, TRUE);
404
405 chooser->scroll_ulc_x = chooser->scroll_ulc_y = 0;
406
407 InvalidateRect (h, &r, TRUE);
408 }
409
410 static void
411 create_listview (HWND dlg, RECT * r)
412 {
413 lv = CreateWindowEx (WS_EX_CLIENTEDGE,
414 "listview",
415 "listviewwindow",
416 WS_CHILD | WS_HSCROLL | WS_VSCROLL | WS_VISIBLE,
417 r->left, r->top,
418 r->right - r->left + 1, r->bottom - r->top + 1,
419 dlg,
420 (HMENU) MAKEINTRESOURCE (IDC_CHOOSE_LIST),
421 hinstance, 0);
422 ShowWindow (lv, SW_SHOW);
423 packagedb db;
424 chooser =
425 new PickView (PickView::views::Category, lv,
426 *db.categories.find("All"));
427
428 default_trust (lv, TRUST_CURR);
429 set_view_mode (lv, PickView::views::Category);
430 if (!SetDlgItemText (dlg, IDC_CHOOSE_VIEWCAPTION, chooser->mode_caption ()))
431 log (LOG_BABBLE) << "Failed to set View button caption %ld" <<
432 GetLastError () << endLog;
433 for (vector <packagemeta *>::iterator i = db.packages.begin ();
434 i != db.packages.end (); ++i)
435 {
436 packagemeta & pkg = **i;
437 pkg.set_requirements (chooser->deftrust);
438 }
439 /* FIXME: do we need to init the desired fields ? */
440 static int ta[] = { IDC_CHOOSE_PREV, IDC_CHOOSE_CURR, IDC_CHOOSE_EXP, 0 };
441 rbset (dlg, ta, IDC_CHOOSE_CURR);
442 }
443
444 static void
445 GetParentRect (HWND parent, HWND child, RECT * r)
446 {
447 POINT p;
448 GetWindowRect (child, r);
449 p.x = r->left;
450 p.y = r->top;
451 ScreenToClient (parent, &p);
452 r->left = p.x;
453 r->top = p.y;
454 p.x = r->right;
455 p.y = r->bottom;
456 ScreenToClient (parent, &p);
457 r->right = p.x;
458 r->bottom = p.y;
459 }
460
461 static void
462 scanAVersion (packageversion version)
463 {
464 if (!version)
465 return;
466 /* Remove mirror sites.
467 * FIXME: This is a bit of a hack. a better way is to abstract
468 * the availability logic to the package
469 */
470 if (!check_for_cached (*(version.source())) && source == IDC_SOURCE_CWD)
471 version.source()->sites.clear();
472 }
473
474 static void
475 scan_downloaded_files ()
476 {
477 /* Look at every known package, in all the known mirror dirs,
478 * and fill in the Cached attribute if it exists.
479 */
480 packagedb db;
481 for (vector <packagemeta *>::iterator n = db.packages.begin ();
482 n != db.packages.end (); ++n)
483 {
484 packagemeta & pkg = **n;
485 for (set<packageversion>::iterator i = pkg.versions.begin ();
486 i != pkg.versions.end (); ++i)
487 {
488 scanAVersion (*i);
489 packageversion foo = *i;
490 packageversion pkgsrcver = foo.sourcePackage();
491 scanAVersion (pkgsrcver);
492 /* For local installs, if there is no src and no bin, the version
493 * is unavailable
494 */
495 if (!i->accessible() && !pkgsrcver.accessible()
496 && *i != pkg.installed)
497 {
498 if (pkg.prev == *i)
499 pkg.prev = packageversion();
500 if (pkg.curr == *i)
501 pkg.curr = packageversion();
502 if (pkg.exp == *i)
503 pkg.exp = packageversion();
504 pkg.versions.erase(i);
505 /* For now, leave the source version alone */
506 }
507 }
508 }
509 /* Don't explicity iterate through sources - any sources that aren't
510 referenced are unselectable anyway
511 */
512 }
513
514 bool
515 ChooserPage::Create ()
516 {
517 return PropertyPage::Create (IDD_CHOOSE);
518 }
519
520 void
521 ChooserPage::OnInit ()
522 {
523 HWND frame;
524 RECT r;
525
526 register_windows (GetInstance ());
527
528 if (source == IDC_SOURCE_DOWNLOAD || source == IDC_SOURCE_CWD)
529 scan_downloaded_files ();
530
531 set_existence ();
532 fill_missing_category ();
533
534 frame = GetDlgItem (IDC_LISTVIEW_POS);
535 choose_inst_text = GetDlgItem (IDC_CHOOSE_INST_TEXT);
536 if (source == IDC_SOURCE_DOWNLOAD)
537 ::SetWindowText (choose_inst_text, "Select packages to download ");
538 else
539 ::SetWindowText (choose_inst_text, "Select packages to install ");
540 GetParentRect (GetHWND (), frame, &r);
541 r.top += 2;
542 r.bottom -= 2;
543 create_listview (GetHWND (), &r);
544 }
545
546 long
547 ChooserPage::OnNext ()
548 {
549 if (source == IDC_SOURCE_CWD)
550 {
551 // Next, install
552 Progress.SetActivateTask (WM_APP_START_INSTALL);
553 }
554 else
555 {
556 // Next, start download from internet
557 Progress.SetActivateTask (WM_APP_START_DOWNLOAD);
558 }
559
560 log (LOG_BABBLE) << "Chooser results..." << endLog;
561 packagedb db;
562 for (vector <packagemeta *>::iterator i = db.packages.begin ();
563 i != db.packages.end (); ++i)
564 {
565 packagemeta & pkg = **i;
566 const char *trust = ((pkg.desired == pkg.prev) ? "prev"
567 : (pkg.desired == pkg.curr) ? "curr"
568 : (pkg.desired == pkg.exp) ? "test" : "unknown");
569 String action = pkg.action_caption ();
570 String const installed =
571 pkg.installed ? pkg.installed.Canonical_version () : "none";
572
573 log (LOG_BABBLE) << "[" << pkg.name << "] action=" << action << " trust=" << trust << " installed=" << installed << " src?=" << (pkg.desired && pkg.desired.sourcePackage().picked() ? "yes" : "no") << endLog;
574 if (pkg.categories.size ())
575 {
576 /* List categories the package belongs to */
577 set <String, String::caseless>::const_iterator i
578 = pkg.categories.begin ();
579 String all_categories = *i;
580 while (i != pkg.categories.end ())
581 all_categories += String (", ") + *(i++);
582
583 log (LOG_BABBLE) << " categories=" << all_categories << endLog;
584 }
585 #if 0
586 if (pkg.desired.required())
587 {
588 /* List other packages this package depends on */
589 Dependency *dp = pkg.desired->required;
590 String requires = dp->package.serialise ();
591 for (dp = dp->next; dp; dp = dp->next)
592 requires += String (", ") + dp->package.serialise ();
593
594 log (LOG_BABBLE) << " requires=" << requires;
595 }
596 #endif
597 pkg.logAllVersions();
598 }
599 return IDD_INSTATUS;
600 }
601
602 long
603 ChooserPage::OnBack ()
604 {
605 initialized = 0;
606 if (source == IDC_SOURCE_CWD)
607 return IDD_LOCAL_DIR;
608 else
609 return IDD_SITE;
610 }
611
612 bool
613 ChooserPage::OnMessageCmd (int id, HWND hwndctl, UINT code)
614 {
615 if (code != BN_CLICKED)
616 {
617 // Not a click notification, we don't care.
618 return false;
619 }
620
621 packagedb db;
622 switch (id)
623 {
624 case IDC_CHOOSE_PREV:
625 if (IsDlgButtonChecked (GetHWND (), id))
626 {
627 default_trust (lv, TRUST_PREV);
628 for (vector <packagemeta *>::iterator i = db.packages.begin ();
629 i != db.packages.end (); ++i)
630 {
631 packagemeta & pkg = **i;
632 pkg.set_requirements (TRUST_PREV);
633 }
634 set_view_mode (lv, chooser->get_view_mode ());
635 break;
636 }
637 else
638 return false;
639 case IDC_CHOOSE_CURR:
640 if (IsDlgButtonChecked (GetHWND (), id))
641 {
642 default_trust (lv, TRUST_CURR);
643 for (vector <packagemeta *>::iterator i = db.packages.begin ();
644 i != db.packages.end (); ++i)
645 {
646 packagemeta & pkg = **i;
647 pkg.set_requirements (TRUST_CURR);
648 }
649 set_view_mode (lv, chooser->get_view_mode ());
650 break;
651 }
652 else
653 return false;
654 case IDC_CHOOSE_EXP:
655 if (IsDlgButtonChecked (GetHWND (), id))
656 {
657 default_trust (lv, TRUST_TEST);
658 for (vector <packagemeta *>::iterator i = db.packages.begin ();
659 i != db.packages.end (); ++i)
660 {
661 packagemeta & pkg = **i;
662 pkg.set_requirements (TRUST_TEST);
663 }
664 set_view_mode (lv, chooser->get_view_mode ());
665 break;
666 }
667 else
668 return false;
669 case IDC_CHOOSE_VIEW:
670 set_view_mode (lv, ++chooser->get_view_mode ());
671 if (!SetDlgItemText
672 (GetHWND (), IDC_CHOOSE_VIEWCAPTION, chooser->mode_caption ()))
673 log (LOG_BABBLE) << "Failed to set View button caption " <<
674 GetLastError () << endLog;
675 break;
676
677
678 default:
679 // Wasn't recognized or handled.
680 return false;
681 }
682
683 // Was handled since we never got to default above.
684 return true;
685 }
This page took 0.065473 seconds and 5 git commands to generate.