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