]> cygwin.com Git - cygwin-apps/setup.git/blob - choose.cc
2001-12-21 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 <stdlib.h>
37 #include <io.h>
38 #include <ctype.h>
39
40 #include "dialog.h"
41 #include "resource.h"
42 #include "state.h"
43 #include "ini.h"
44 #include "concat.h"
45 #include "msg.h"
46 #include "log.h"
47 #include "find.h"
48 #include "filemanip.h"
49 #include "io_stream.h"
50 #include "choose.h"
51 #include "category.h"
52
53 #include "package_db.h"
54 #include "package_meta.h"
55 #include "package_version.h"
56
57 #include "port.h"
58
59 #define alloca __builtin_alloca
60
61 #define HMARGIN 10
62 #define ROW_MARGIN 5
63 #define ICON_MARGIN 4
64 #define RTARROW_WIDTH 11
65 #define SPIN_WIDTH 11
66 #define NEW_COL_SIZE_SLOP (ICON_MARGIN + SPIN_WIDTH + RTARROW_WIDTH)
67
68 #define CHECK_SIZE 11
69
70 static int initialized = 0;
71
72 static int scroll_ulc_x, scroll_ulc_y;
73
74 static HWND lv, nextbutton, choose_inst_text;
75 static TEXTMETRIC tm;
76 static int header_height;
77 static HANDLE sysfont;
78 static int row_height;
79 static HANDLE bm_spin, bm_rtarrow, bm_checkyes, bm_checkno, bm_checkna;
80 static HDC bitmap_dc;
81 static view *chooser = NULL;
82 static trusts deftrust = TRUST_UNKNOWN;
83
84 static struct _header pkg_headers[] = {
85 {"Current", 7, 0, 0},
86 {"New", 3, 0, 0},
87 {"Src?", 4, 0, 0},
88 {"Category", 8, 0, 0},
89 {"Package", 7, 0, 0},
90 {0, 0, 0, 0}
91 };
92
93 static struct _header cat_headers[] = {
94 {"Category", 8, 0, 0},
95 {"Current", 7, 0, 0},
96 {"New", 3, 0, 0},
97 {"Src?", 4, 0, 0},
98 {"Package", 7, 0, 0},
99 {0, 0, 0, 0}
100 };
101
102 static void set_view_mode (HWND h, views mode);
103
104 packageversion *
105 pkgtrustp (packagemeta const &pkg, trusts const t)
106 {
107 return t == TRUST_PREV ? pkg.prev : t == TRUST_CURR ? pkg.curr : pkg.exp;
108 }
109
110 static int
111 add_required (packagemeta & pkg, size_t depth = 0)
112 {
113 Dependency *dp;
114 packagemeta *required;
115 int changed = 0;
116 if (!pkg.desired
117 || (pkg.desired != pkg.installed && !pkg.desired->binpicked))
118 /* uninstall || source only */
119 return 0;
120
121 dp = pkg.desired->required;
122 packagedb db;
123 /* cheap test for too much recursion */
124 if (depth > 5)
125 return 0;
126 while (dp)
127 {
128 if ((required = db.packages.getbykey (dp->package)) == NULL)
129 {
130 dp = dp->next;
131 changed++;
132 continue;
133 }
134 if (!required->desired)
135 {
136 /* it's set to uninstall */
137 required->set_action (pkgtrustp (*required, deftrust));
138 }
139 else if (required->desired != required->installed
140 && !required->desired->binpicked)
141 {
142 /* it's set to change to a different version source only */
143 required->desired->binpicked = 1;
144 }
145 /* does this requirement have requirements? */
146 changed += add_required (*required, depth + 1);
147 dp = dp->next;
148 }
149 return changed;
150 }
151
152 void
153 topbucket::paint (HDC hdc, int x, int y, int row, int show_cat)
154 {
155 int accum_row = row;
156 for (size_t n = 1; n <= bucket.number (); n++)
157 {
158 bucket[n]->paint (hdc, x, y, accum_row, show_cat);
159 accum_row += bucket[n]->itemcount ();
160 }
161 }
162
163 void
164 topbucket::empty (void)
165 {
166 while (bucket.number ())
167 {
168 pick_line *line = bucket.removebyindex (1);
169 delete line;
170 }
171 }
172
173 topbucket::~topbucket (void)
174 {
175 empty ();
176 }
177
178 int
179 topbucket::click (int const myrow, int const ClickedRow, int const x)
180 {
181 int accum_row = myrow;
182 for (size_t n = 1; n <= bucket.number (); n++)
183 {
184 accum_row += bucket[n]->itemcount ();
185 if (accum_row > ClickedRow)
186 return bucket[n]->click (accum_row - bucket[n]->itemcount (),
187 ClickedRow, x);
188 }
189 return 0;
190 }
191
192 static void
193 paint (HWND hwnd)
194 {
195 HDC hdc;
196 PAINTSTRUCT ps;
197 int x, y;
198
199 hdc = BeginPaint (hwnd, &ps);
200
201 SelectObject (hdc, sysfont);
202 SetBkColor (hdc, GetSysColor (COLOR_WINDOW));
203 SetTextColor (hdc, GetSysColor (COLOR_WINDOWTEXT));
204
205 RECT cr;
206 GetClientRect (hwnd, &cr);
207
208 x = cr.left - scroll_ulc_x;
209 y = cr.top - scroll_ulc_y + header_height;
210
211 IntersectClipRect (hdc, cr.left, cr.top + header_height, cr.right,
212 cr.bottom);
213
214 chooser->contents.paint (hdc, x, y, 0, (chooser->get_view_mode () ==
215 VIEW_CATEGORY) ? 0 : 1);
216
217 if (chooser->contents.itemcount () == 0)
218 {
219 static const char *msg = "Nothing to Install/Update";
220 if (source == IDC_SOURCE_DOWNLOAD)
221 msg = "Nothing to Download";
222 TextOut (hdc, HMARGIN, header_height, msg, strlen (msg));
223 }
224
225 EndPaint (hwnd, &ps);
226 }
227
228 void
229 view::scroll (HWND hwnd, int which, int *var, int code)
230 {
231 SCROLLINFO si;
232 si.cbSize = sizeof (si);
233 si.fMask = SIF_ALL;
234 GetScrollInfo (hwnd, which, &si);
235
236 switch (code)
237 {
238 case SB_THUMBTRACK:
239 si.nPos = si.nTrackPos;
240 break;
241 case SB_THUMBPOSITION:
242 break;
243 case SB_BOTTOM:
244 si.nPos = si.nMax;
245 break;
246 case SB_TOP:
247 si.nPos = 0;
248 break;
249 case SB_LINEDOWN:
250 si.nPos += row_height;
251 break;
252 case SB_LINEUP:
253 si.nPos -= row_height;
254 break;
255 case SB_PAGEDOWN:
256 si.nPos += si.nPage * 9 / 10;
257 break;
258 case SB_PAGEUP:
259 si.nPos -= si.nPage * 9 / 10;
260 break;
261 }
262
263 if ((int) si.nPos < 0)
264 si.nPos = 0;
265 if (si.nPos + si.nPage > (unsigned int) si.nMax)
266 si.nPos = si.nMax - si.nPage;
267
268 si.fMask = SIF_POS;
269 SetScrollInfo (hwnd, which, &si, TRUE);
270
271 int ox = scroll_ulc_x;
272 int oy = scroll_ulc_y;
273 *var = si.nPos;
274
275 RECT cr, sr;
276 GetClientRect (hwnd, &cr);
277 sr = cr;
278 sr.top += header_height;
279 UpdateWindow (hwnd);
280 ScrollWindow (hwnd, ox - scroll_ulc_x, oy - scroll_ulc_y, &sr, &sr);
281 /*
282 sr.bottom = sr.top;
283 sr.top = cr.top;
284 ScrollWindow (hwnd, ox - scroll_ulc_x, 0, &sr, &sr);
285 */
286 if (ox - scroll_ulc_x)
287 {
288 GetClientRect (listheader, &cr);
289 sr = cr;
290 // UpdateWindow (htmp);
291 MoveWindow (listheader, -scroll_ulc_x, 0,
292 chooser->headers[last_col].x +
293 chooser->headers[last_col].width, header_height, TRUE);
294 }
295 UpdateWindow (hwnd);
296 }
297
298 static LRESULT CALLBACK
299 list_vscroll (HWND hwnd, HWND hctl, UINT code, int pos)
300 {
301 chooser->scroll (hwnd, SB_VERT, &scroll_ulc_y, code);
302 return 0;
303 }
304
305 static LRESULT CALLBACK
306 list_hscroll (HWND hwnd, HWND hctl, UINT code, int pos)
307 {
308 chooser->scroll (hwnd, SB_HORZ, &scroll_ulc_x, code);
309 return 0;
310 }
311
312 static LRESULT CALLBACK
313 list_click (HWND hwnd, BOOL dblclk, int x, int y, UINT hitCode)
314 {
315 int row, refresh;
316
317 if (chooser->contents.itemcount () == 0)
318 return 0;
319
320 if (y < header_height)
321 return 0;
322 x += scroll_ulc_x;
323 y += scroll_ulc_y - header_height;
324
325 row = (y + ROW_MARGIN / 2) / row_height;
326
327 if (row < 0 || row >= chooser->contents.itemcount ())
328 return 0;
329
330 refresh = chooser->click (row, x);
331
332 if (refresh)
333 {
334 RECT r;
335 GetClientRect (lv, &r);
336 SCROLLINFO si;
337 memset (&si, 0, sizeof (si));
338 si.cbSize = sizeof (si);
339 si.fMask = SIF_ALL; /* SIF_RANGE was giving strange behaviour */
340 si.nMin = 0;
341
342 si.nMax = chooser->contents.itemcount () * row_height;
343 si.nPage = r.bottom - header_height;
344
345 /* if we are under the minimum display count ,
346 * set the offset to 0
347 */
348 if ((unsigned int) si.nMax <= si.nPage)
349 scroll_ulc_y = 0;
350 si.nPos = scroll_ulc_y;
351
352 SetScrollInfo (lv, SB_VERT, &si, TRUE);
353
354 InvalidateRect (lv, &r, TRUE);
355
356 }
357 else
358 {
359 RECT rect;
360 rect.left = chooser->headers[chooser->new_col].x - scroll_ulc_x;
361 rect.right = chooser->headers[chooser->src_col + 1].x - scroll_ulc_x;
362 rect.top = header_height + row * row_height - scroll_ulc_y;
363 rect.bottom = rect.top + row_height;
364 InvalidateRect (hwnd, &rect, TRUE);
365 }
366 return 0;
367 }
368
369 static LRESULT CALLBACK
370 listview_proc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
371 {
372 switch (message)
373 {
374 case WM_HSCROLL:
375 return HANDLE_WM_HSCROLL (hwnd, wParam, lParam, list_hscroll);
376 case WM_VSCROLL:
377 return HANDLE_WM_VSCROLL (hwnd, wParam, lParam, list_vscroll);
378 case WM_LBUTTONDOWN:
379 return HANDLE_WM_LBUTTONDOWN (hwnd, wParam, lParam, list_click);
380 case WM_PAINT:
381 paint (hwnd);
382 return 0;
383 case WM_NOTIFY:
384 {
385 // pnmh = (LPNMHDR) lParam
386 LPNMHEADER phdr = (LPNMHEADER) lParam;
387 switch (phdr->hdr.code)
388 {
389 case HDN_ITEMCHANGED:
390 if (phdr->hdr.hwndFrom == chooser->ListHeader ())
391 {
392 if (phdr->pitem && phdr->pitem->mask & HDI_WIDTH)
393 chooser->headers[phdr->iItem].width = phdr->pitem->cxy;
394 for (int i = 1; i <= chooser->last_col; i++)
395 chooser->headers[i].x =
396 chooser->headers[i - 1].x + chooser->headers[i - 1].width;
397 RECT r;
398 GetClientRect (hwnd, &r);
399 SCROLLINFO si;
400 si.cbSize = sizeof (si);
401 si.fMask = SIF_ALL;
402 GetScrollInfo (hwnd, SB_HORZ, &si);
403 int oldMax = si.nMax;
404 si.nMax =
405 chooser->headers[chooser->last_col].x +
406 chooser->headers[chooser->last_col].width;
407 if (si.nTrackPos && oldMax > si.nMax)
408 si.nTrackPos += si.nMax - oldMax;
409 si.nPage = r.right;
410 SetScrollInfo (hwnd, SB_HORZ, &si, TRUE);
411 InvalidateRect (hwnd, &r, TRUE);
412 if (si.nTrackPos && oldMax > si.nMax)
413 chooser->scroll (hwnd, SB_HORZ, &scroll_ulc_x,
414 SB_THUMBTRACK);
415 }
416 break;
417 default:
418 break;
419 }
420 }
421 default:
422 return DefWindowProc (hwnd, message, wParam, lParam);
423 }
424 }
425
426 static void
427 register_windows (HINSTANCE hinst)
428 {
429 WNDCLASSEX wcex;
430 static int done = 0;
431
432 if (done)
433 return;
434 done = 1;
435
436 memset (&wcex, 0, sizeof (wcex));
437 wcex.cbSize = sizeof (WNDCLASSEX);
438 wcex.style = CS_HREDRAW | CS_VREDRAW;
439 wcex.lpfnWndProc = listview_proc;
440 wcex.hInstance = hinst;
441 wcex.hIcon = LoadIcon (0, IDI_APPLICATION);
442 wcex.hCursor = LoadCursor (0, IDC_ARROW);
443 wcex.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
444 wcex.lpszClassName = "listview";
445
446 RegisterClassEx (&wcex);
447 }
448
449 static void
450 note_width (struct _header *hdrs, HDC dc, const char *string, int addend,
451 int column)
452 {
453 if (!string)
454 {
455 if (hdrs[column].width < addend)
456 hdrs[column].width = addend;
457 return;
458 }
459 SIZE s;
460 GetTextExtentPoint32 (dc, string, strlen (string), &s);
461 if (hdrs[column].width < s.cx + addend)
462 hdrs[column].width = s.cx + addend;
463 }
464
465 static void
466 set_existence ()
467 {
468 /* FIXME:
469 iterate through the package list, and delete packages that are
470 * Not installed
471 * have no mirror site
472 and then do the same for categories with no packages.
473 */
474 }
475
476 static void
477 fill_missing_category ()
478 {
479 packagedb db;
480 for (size_t n = 1; n < db.packages.number (); n++)
481 {
482 packagemeta & pkg = *db.packages[n];
483 if (!pkg.Categories.number ())
484 pkg.add_category (db.categories.registerbykey ("Misc"));
485 }
486 }
487
488 static void
489 default_trust (HWND h, trusts trust)
490 {
491 deftrust = trust;
492 packagedb db;
493 for (size_t n = 1; n < db.packages.number (); n++)
494 {
495 packagemeta & pkg = *db.packages[n];
496 if (pkg.installed
497 || pkg.Categories.getbykey (db.categories.registerbykey ("Base"))
498 || pkg.Categories.getbykey (db.categories.registerbykey ("Misc")))
499 {
500 pkg.desired = pkgtrustp (pkg, trust);
501 if (pkg.desired)
502 {
503 pkg.desired->binpicked = pkg.desired == pkg.installed ? 0 : 1;
504 pkg.desired->srcpicked = 0;
505 }
506 }
507 else
508 pkg.desired = 0;
509 }
510 RECT r;
511 GetClientRect (h, &r);
512 InvalidateRect (h, &r, TRUE);
513 if (nextbutton)
514 SetFocus (nextbutton);
515 }
516
517 void
518 pick_pkg_line::paint (HDC hdc, int x, int y, int row, int show_cat)
519 {
520 int r = y + row * row_height;
521 int by = r + tm.tmHeight - 11;
522 int oldDC = SaveDC (hdc);
523 if (!oldDC)
524 return;
525 HRGN oldClip = CreateRectRgn (0, 0, 0, 0);
526 if (GetRandomRgn (hdc, oldClip, SYSRGN) == -1)
527 {
528 RestoreDC (hdc, oldDC);
529 return;
530 }
531 unsigned int regionsize = GetRegionData (oldClip, 0, 0);
532 LPRGNDATA oldClipData = (LPRGNDATA) malloc (regionsize);
533 if (GetRegionData (oldClip, regionsize, oldClipData) != regionsize)
534 {
535 RestoreDC (hdc, oldDC);
536 DeleteObject (oldClip);
537 return;
538 }
539 for (unsigned int n = 0; n < oldClipData->rdh.nCount; n++)
540 for (unsigned int t = 0; t < 2; t++)
541 ScreenToClient (WindowFromDC (hdc),
542 &((POINT *) oldClipData->Buffer)[t + n * 2]);
543
544 HRGN oldClip2 = ExtCreateRegion (NULL, regionsize, oldClipData);
545 SelectClipRgn (hdc, oldClip2);
546 if (pkg.installed)
547 {
548 IntersectClipRect (hdc, x + chooser->headers[chooser->current_col].x,
549 by,
550 x + chooser->headers[chooser->current_col].x +
551 chooser->headers[chooser->current_col].width,
552 by + 11);
553 TextOut (hdc,
554 x + chooser->headers[chooser->current_col].x + HMARGIN / 2, r,
555 pkg.installed->Canonical_version (),
556 strlen (pkg.installed->Canonical_version ()));
557 SelectObject (bitmap_dc, bm_rtarrow);
558 BitBlt (hdc, x + chooser->headers[chooser->new_col].x + HMARGIN / 2,
559 by, 11, 11, bitmap_dc, 0, 0, SRCCOPY);
560 SelectClipRgn (hdc, oldClip2);
561 }
562
563 const char *s = pkg.action_caption ();
564 IntersectClipRect (hdc, x + chooser->headers[chooser->new_col].x,
565 by,
566 x + chooser->headers[chooser->new_col].x +
567 chooser->headers[chooser->new_col].width, by + 11);
568 TextOut (hdc,
569 x + chooser->headers[chooser->new_col].x + HMARGIN / 2 +
570 NEW_COL_SIZE_SLOP, r, s, strlen (s));
571 SelectObject (bitmap_dc, bm_spin);
572 BitBlt (hdc,
573 x + chooser->headers[chooser->new_col].x + ICON_MARGIN / 2 +
574 RTARROW_WIDTH + HMARGIN / 2, by, 11, 11, bitmap_dc, 0, 0, SRCCOPY);
575 SelectClipRgn (hdc, oldClip2);
576
577 HANDLE check_bm;
578 if ( /* uninstall */ !pkg.desired ||
579 /* source only */ (!pkg.desired->binpicked
580 && pkg.desired->srcpicked) ||
581 /* when no source mirror available */
582 !pkg.desired->src.sites.number ())
583 check_bm = bm_checkna;
584 else if (pkg.desired->srcpicked)
585 check_bm = bm_checkyes;
586 else
587 check_bm = bm_checkno;
588
589 SelectObject (bitmap_dc, check_bm);
590 IntersectClipRect (hdc, x + chooser->headers[chooser->src_col].x, by,
591 x + chooser->headers[chooser->src_col].x +
592 chooser->headers[chooser->src_col].width, by + 11);
593 BitBlt (hdc, x + chooser->headers[chooser->src_col].x + HMARGIN / 2, by, 11,
594 11, bitmap_dc, 0, 0, SRCCOPY);
595 SelectClipRgn (hdc, oldClip2);
596
597 /* shows "first" category - do we want to show any? */
598 if (pkg.Categories.number () && show_cat)
599 {
600 IntersectClipRect (hdc, x + chooser->headers[chooser->cat_col].x, by,
601 x + chooser->headers[chooser->cat_col].x +
602 chooser->headers[chooser->cat_col].x, by + 11);
603 TextOut (hdc, x + chooser->headers[chooser->cat_col].x + HMARGIN / 2, r,
604 pkg.Categories[1]->key.name,
605 strlen (pkg.Categories[1]->key.name));
606 SelectClipRgn (hdc, oldClip2);
607 }
608
609 if (!pkg.SDesc ())
610 s = pkg.name;
611 else
612 {
613 static char buf[512];
614 strcpy (buf, pkg.name);
615 strcat (buf, ": ");
616 strcat (buf, pkg.SDesc ());
617 s = buf;
618 }
619 IntersectClipRect (hdc, x + chooser->headers[chooser->pkg_col].x, by,
620 x + chooser->headers[chooser->pkg_col].x +
621 chooser->headers[chooser->pkg_col].width, by + 11);
622 TextOut (hdc, x + chooser->headers[chooser->pkg_col].x + HMARGIN / 2, r, s,
623 strlen (s));
624 DeleteObject (oldClip);
625 DeleteObject (oldClip2);
626 RestoreDC (hdc, oldDC);
627 }
628
629 void
630 pick_category_line::paint (HDC hdc, int x, int y, int row, int show_cat)
631 {
632 int r = y + row * row_height;
633 TextOut (hdc, x + chooser->headers[chooser->cat_col].x + HMARGIN / 2, r,
634 cat.name, strlen (cat.name));
635 if (collapsed)
636 return;
637 int accum_row = row + 1;
638 for (size_t n = 1; n <= bucket.number (); n++)
639 {
640 bucket[n]->paint (hdc, x, y, accum_row, show_cat);
641 accum_row += bucket[n]->itemcount ();
642 }
643 }
644
645 int
646 pick_pkg_line::click (int const myrow, int const ClickedRow, int const x)
647 {
648 // assert (myrow == ClickedRow);
649 if (pkg.desired && pkg.desired->src.sites.number ()
650 && x >= chooser->headers[chooser->src_col].x - HMARGIN / 2
651 && x <= chooser->headers[chooser->src_col + 1].x - HMARGIN / 2)
652 pkg.desired->srcpicked ^= 1;
653
654 if (x >= chooser->headers[chooser->new_col].x - HMARGIN / 2
655 && x <= chooser->headers[chooser->new_col + 1].x - HMARGIN / 2)
656 {
657 pkg.set_action (pkgtrustp (pkg, deftrust));
658 /* Add any packages that are needed by this package */
659 return add_required (pkg);
660 }
661 return 0;
662 }
663
664 int
665 pick_category_line::click (int const myrow, int const ClickedRow, int const x)
666 {
667 if (myrow == ClickedRow)
668 {
669 collapsed = !collapsed;
670 int accum_row = 0;
671 for (size_t n = 1; n <= bucket.number (); n++)
672 accum_row += bucket[n]->itemcount ();
673 return collapsed ? accum_row : -accum_row;
674 }
675 else
676 {
677 int accum_row = myrow + 1;
678 for (size_t n = 1; n <= bucket.number (); n++)
679 {
680 if (accum_row + bucket[n]->itemcount () > ClickedRow)
681 return bucket[n]->click (accum_row, ClickedRow, x);
682 accum_row += bucket[n]->itemcount ();
683 }
684 return 0;
685 }
686 }
687
688 HWND DoCreateHeader (HWND hwndParent);
689
690 view::view (views _mode, HWND lv):listview (lv)
691 {
692
693 HDC
694 dc =
695 GetDC (listview);
696 sysfont = GetStockObject (DEFAULT_GUI_FONT);
697 SelectObject (dc, sysfont);
698 GetTextMetrics (dc, &tm);
699
700 bitmap_dc = CreateCompatibleDC (dc);
701
702 row_height = (tm.tmHeight + tm.tmExternalLeading + ROW_MARGIN);
703 int
704 irh =
705 tm.
706 tmExternalLeading +
707 tm.
708 tmDescent +
709 11 +
710 ROW_MARGIN;
711 if (row_height < irh)
712 row_height = irh;
713
714 RECT
715 rcParent;
716 HDLAYOUT
717 hdl;
718 WINDOWPOS
719 wp;
720
721 // Ensure that the common control DLL is loaded, and then create
722 // the header control.
723 INITCOMMONCONTROLSEX
724 controlinfo = {
725 sizeof (INITCOMMONCONTROLSEX),
726 ICC_LISTVIEW_CLASSES
727 };
728 InitCommonControlsEx (&controlinfo);
729
730 if ((listheader = CreateWindowEx (0, WC_HEADER, (LPCTSTR) NULL,
731 WS_CHILD | WS_BORDER | CCS_NORESIZE |
732 // | HDS_BUTTONS
733 HDS_HORZ, 0, 0, 0, 0, listview,
734 (HMENU) IDC_CHOOSE_LISTHEADER, hinstance,
735 (LPVOID) NULL)) == NULL)
736 // FIXME: throw an exception
737 exit (10);
738
739 // Retrieve the bounding rectangle of the parent window's
740 // client area, and then request size and position values
741 // from the header control.
742 GetClientRect (listview, &rcParent);
743
744 hdl.prc = &rcParent;
745 hdl.pwpos = &wp;
746 if (!SendMessage (listheader, HDM_LAYOUT, 0, (LPARAM) & hdl))
747 // FIXME: throw an exception
748 exit (11);
749
750
751 // Set the size, position, and visibility of the header control.
752 SetWindowPos (listheader, wp.hwndInsertAfter, wp.x, wp.y,
753 wp.cx, wp.cy, wp.flags | SWP_SHOWWINDOW);
754
755 header_height = wp.cy;
756
757 view_mode = VIEW_PACKAGE;
758 set_headers ();
759 init_headers (dc);
760 view_mode = VIEW_CATEGORY;
761 set_headers ();
762 init_headers (dc);
763
764 view_mode = _mode;
765 set_headers ();
766
767 ReleaseDC (lv, dc);
768 }
769
770 void
771 view::set_view_mode (views _mode)
772 {
773 if (_mode == NVIEW)
774 view_mode = VIEW_PACKAGE_FULL;
775 else
776 view_mode = _mode;
777 set_headers ();
778 }
779
780 const char *
781 view::mode_caption ()
782 {
783 switch (view_mode)
784 {
785 case VIEW_UNKNOWN:
786 return "";
787 case VIEW_PACKAGE_FULL:
788 return "Full";
789 case VIEW_PACKAGE:
790 return "Partial";
791 case VIEW_CATEGORY:
792 return "Category";
793 default:
794 return "";
795 }
796 }
797
798 int DoInsertItem (HWND hwndHeader, int iInsertAfter, int nWidth, LPSTR lpsz);
799
800 void
801 view::set_headers ()
802 {
803 switch (view_mode)
804 {
805 case VIEW_UNKNOWN:
806 return;
807 case VIEW_PACKAGE_FULL:
808 case VIEW_PACKAGE:
809 headers = pkg_headers;
810 current_col = 0;
811 new_col = 1;
812 src_col = 2;
813 cat_col = 3;
814 pkg_col = 4;
815 last_col = 4;
816 break;
817 case VIEW_CATEGORY:
818 headers = cat_headers;
819 current_col = 1;
820 new_col = 2;
821 src_col = 3;
822 cat_col = 0;
823 pkg_col = 4;
824 last_col = 4;
825 break;
826 default:
827 return;
828 }
829 while (int n = SendMessage (listheader, HDM_GETITEMCOUNT, 0, 0))
830 {
831 SendMessage (listheader, HDM_DELETEITEM, n - 1, 0);
832 }
833 int i;
834 for (i = 0; i <= last_col; i++)
835 DoInsertItem (listheader, i, headers[i].width, (char *) headers[i].text);
836 }
837
838
839 void
840 view::init_headers (HDC dc)
841 {
842 int i;
843
844 for (i = 0; headers[i].text; i++)
845 {
846 headers[i].width = 0;
847 headers[i].x = 0;
848 }
849
850 for (i = 0; headers[i].text; i++)
851 note_width (headers, dc, headers[i].text, HMARGIN, i);
852 /* src checkbox */
853 note_width (headers, dc, 0, HMARGIN + 11, src_col);
854 packagedb db;
855 for (size_t n = 1; n < db.packages.number (); n++)
856 {
857 packagemeta & pkg = *db.packages[n];
858 if (pkg.installed)
859 note_width (headers, dc, pkg.installed->Canonical_version (),
860 HMARGIN, current_col);
861 for (size_t n = 1; n <= pkg.versions.number (); n++)
862 if (pkg.versions[n] != pkg.installed)
863 note_width (headers, dc,
864 pkg.versions[n]->Canonical_version (),
865 NEW_COL_SIZE_SLOP + HMARGIN, new_col);
866 for (size_t n = 1; n <= db.categories.number (); n++)
867 note_width (headers, dc, db.categories[n]->name, HMARGIN, cat_col);
868 if (!pkg.SDesc ())
869 note_width (headers, dc, pkg.name, HMARGIN, pkg_col);
870 else
871 {
872 static char buf[512];
873 strcpy (buf, pkg.name);
874 strcat (buf, ": ");
875 strcat (buf, pkg.SDesc ());
876 note_width (headers, dc, buf, HMARGIN, pkg_col);
877 }
878 }
879 note_width (headers, dc, "keep", NEW_COL_SIZE_SLOP + HMARGIN, new_col);
880 note_width (headers, dc, "uninstall", NEW_COL_SIZE_SLOP + HMARGIN, new_col);
881
882 headers[0].x = 0;
883 for (i = 1; i <= last_col; i++)
884 headers[i].x = headers[i - 1].x + headers[i - 1].width;
885 }
886
887 void
888 view::insert_pkg (packagemeta & pkg)
889 {
890 if (view_mode != VIEW_CATEGORY)
891 {
892 pick_pkg_line & line = *new pick_pkg_line (pkg);
893 contents.insert (line);
894 }
895 else
896 {
897 for (size_t x = 1; x <= pkg.Categories.number (); x++)
898 {
899 Category & cat = pkg.Categories[x]->key;
900 pick_category_line & catline = *new pick_category_line (cat);
901 pick_pkg_line & line = *new pick_pkg_line (pkg);
902 catline.insert (line);
903 contents.insert (catline);
904 }
905 }
906 }
907
908 void
909 view::insert_category (Category * cat, bool collapsed)
910 {
911 pick_category_line & catline = *new pick_category_line (*cat, collapsed);
912 for (CategoryPackage * catpkg = cat->packages; catpkg;
913 catpkg = catpkg->next)
914 {
915 pick_pkg_line & line = *new pick_pkg_line (*catpkg->pkg);
916 catline.insert (line);
917 }
918 contents.insert (catline);
919 }
920
921 void
922 view::clear_view (void)
923 {
924 contents.empty ();
925 }
926
927 static views
928 viewsplusplus (views theview)
929 {
930 switch (theview)
931 {
932 case VIEW_UNKNOWN:
933 return VIEW_PACKAGE_FULL;
934 case VIEW_PACKAGE_FULL:
935 return VIEW_PACKAGE;
936 case VIEW_PACKAGE:
937 return VIEW_CATEGORY;
938 case VIEW_CATEGORY:
939 return NVIEW;
940 default:
941 return VIEW_UNKNOWN;
942 }
943 }
944
945 int
946 view::click (int row, int x)
947 {
948 return contents.click (0, row, x);
949 }
950
951 static void
952 set_view_mode (HWND h, views mode)
953 {
954 chooser->set_view_mode (mode);
955
956 chooser->clear_view ();
957 packagedb db;
958 switch (chooser->get_view_mode ())
959 {
960 case VIEW_PACKAGE:
961 for (size_t n = 1; n < db.packages.number (); n++)
962 {
963 packagemeta & pkg = *db.packages[n];
964 if ((!pkg.desired && pkg.installed)
965 || (pkg.desired
966 && (pkg.desired->srcpicked || pkg.desired->binpicked)))
967 chooser->insert_pkg (pkg);
968 }
969 break;
970 case VIEW_PACKAGE_FULL:
971 for (size_t n = 1; n < db.packages.number (); n++)
972 {
973 packagemeta & pkg = *db.packages[n];
974 chooser->insert_pkg (pkg);
975 }
976 break;
977 case VIEW_CATEGORY:
978 /* start collapsed. TODO: make this a chooser flag */
979 for (size_t n = 1; n <= db.categories.number (); n++)
980 chooser->insert_category (db.categories[n], CATEGORY_COLLAPSED);
981 break;
982 default:
983 break;
984 }
985
986 RECT r;
987 GetClientRect (h, &r);
988 SCROLLINFO si;
989 memset (&si, 0, sizeof (si));
990 si.cbSize = sizeof (si);
991 si.fMask = SIF_ALL;
992 si.nMin = 0;
993 si.nMax = chooser->headers[chooser->last_col].x + chooser->headers[chooser->last_col].width; // + HMARGIN;
994 si.nPage = r.right;
995 SetScrollInfo (h, SB_HORZ, &si, TRUE);
996
997 si.nMax = chooser->contents.itemcount () * row_height;
998 si.nPage = r.bottom - header_height;
999 SetScrollInfo (h, SB_VERT, &si, TRUE);
1000
1001 scroll_ulc_x = scroll_ulc_y = 0;
1002
1003 InvalidateRect (h, &r, TRUE);
1004
1005 if (nextbutton)
1006 SetFocus (nextbutton);
1007 }
1008
1009 // DoInsertItem - inserts an item into a header control.
1010 // Returns the index of the new item.
1011 // hwndHeader - handle to the header control.
1012 // iInsertAfter - index of the previous item.
1013 // nWidth - width of the new item.
1014 // lpsz - address of the item string.
1015 int
1016 DoInsertItem (HWND hwndHeader, int iInsertAfter, int nWidth, LPSTR lpsz)
1017 {
1018 HDITEM hdi;
1019 int index;
1020
1021 hdi.mask = HDI_TEXT | HDI_FORMAT | HDI_WIDTH;
1022 hdi.pszText = lpsz;
1023 hdi.cxy = nWidth;
1024 hdi.cchTextMax = lstrlen (hdi.pszText);
1025 hdi.fmt = HDF_LEFT | HDF_STRING;
1026
1027 index = SendMessage (hwndHeader, HDM_INSERTITEM,
1028 (WPARAM) iInsertAfter, (LPARAM) & hdi);
1029
1030 return index;
1031
1032 }
1033
1034
1035 static void
1036 create_listview (HWND dlg, RECT * r)
1037 {
1038 lv = CreateWindowEx (WS_EX_CLIENTEDGE,
1039 "listview",
1040 "listviewwindow",
1041 WS_CHILD | WS_HSCROLL | WS_VSCROLL | WS_VISIBLE,
1042 r->left, r->top,
1043 r->right - r->left + 1, r->bottom - r->top + 1,
1044 dlg,
1045 (HMENU) MAKEINTRESOURCE (IDC_CHOOSE_LIST),
1046 hinstance, 0);
1047 ShowWindow (lv, SW_SHOW);
1048 chooser = new view (VIEW_CATEGORY, lv);
1049
1050 default_trust (lv, TRUST_CURR);
1051 set_view_mode (lv, VIEW_CATEGORY);
1052 if (!SetDlgItemText (dlg, IDC_CHOOSE_VIEWCAPTION, chooser->mode_caption ()))
1053 log (LOG_BABBLE, "Failed to set View button caption %ld",
1054 GetLastError ());
1055 packagedb db;
1056 for (size_t n = 1; n < db.packages.number (); n++)
1057 {
1058 packagemeta & pkg = *db.packages[n];
1059 add_required (pkg);
1060 }
1061 /* FIXME: do we need to init the desired fields ? */
1062 static int ta[] = { IDC_CHOOSE_CURR, 0 };
1063 rbset (dlg, ta, IDC_CHOOSE_CURR);
1064
1065 }
1066
1067 static BOOL
1068 dialog_cmd (HWND h, int id, HWND hwndctl, UINT code)
1069 {
1070 packagedb db;
1071 switch (id)
1072 {
1073 case IDC_CHOOSE_PREV:
1074 default_trust (lv, TRUST_PREV);
1075 for (size_t n = 1; n < db.packages.number (); n++)
1076 {
1077 packagemeta & pkg = *db.packages[n];
1078 add_required (pkg);
1079 }
1080 set_view_mode (lv, chooser->get_view_mode ());
1081 break;
1082 case IDC_CHOOSE_CURR:
1083 default_trust (lv, TRUST_CURR);
1084 for (size_t n = 1; n < db.packages.number (); n++)
1085 {
1086 packagemeta & pkg = *db.packages[n];
1087 add_required (pkg);
1088 }
1089 set_view_mode (lv, chooser->get_view_mode ());
1090 break;
1091 case IDC_CHOOSE_EXP:
1092 default_trust (lv, TRUST_TEST);
1093 for (size_t n = 1; n < db.packages.number (); n++)
1094 {
1095 packagemeta & pkg = *db.packages[n];
1096 add_required (pkg);
1097 }
1098 set_view_mode (lv, chooser->get_view_mode ());
1099 break;
1100 case IDC_CHOOSE_VIEW:
1101 set_view_mode (lv, viewsplusplus (chooser->get_view_mode ()));
1102 if (!SetDlgItemText
1103 (h, IDC_CHOOSE_VIEWCAPTION, chooser->mode_caption ()))
1104 log (LOG_BABBLE, "Failed to set View button caption %ld",
1105 GetLastError ());
1106 break;
1107
1108 case IDOK:
1109 if (source == IDC_SOURCE_CWD)
1110 NEXT (IDD_S_INSTALL);
1111 else
1112 NEXT (IDD_S_DOWNLOAD);
1113 break;
1114
1115 case IDC_BACK:
1116 initialized = 0;
1117 if (source == IDC_SOURCE_CWD)
1118 NEXT (IDD_LOCAL_DIR);
1119 else
1120 NEXT (IDD_SITE);
1121 break;
1122
1123 case IDCANCEL:
1124 NEXT (0);
1125 break;
1126 }
1127 return 0;
1128 }
1129
1130 static void
1131 GetParentRect (HWND parent, HWND child, RECT * r)
1132 {
1133 POINT p;
1134 GetWindowRect (child, r);
1135 p.x = r->left;
1136 p.y = r->top;
1137 ScreenToClient (parent, &p);
1138 r->left = p.x;
1139 r->top = p.y;
1140 p.x = r->right;
1141 p.y = r->bottom;
1142 ScreenToClient (parent, &p);
1143 r->right = p.x;
1144 r->bottom = p.y;
1145 }
1146
1147 static BOOL CALLBACK
1148 dialog_proc (HWND h, UINT message, WPARAM wParam, LPARAM lParam)
1149 {
1150 HWND frame;
1151 RECT r;
1152 switch (message)
1153 {
1154 case WM_INITDIALOG:
1155 nextbutton = GetDlgItem (h, IDOK);
1156 frame = GetDlgItem (h, IDC_LISTVIEW_POS);
1157 choose_inst_text = GetDlgItem (h, IDC_CHOOSE_INST_TEXT);
1158 if (source == IDC_SOURCE_DOWNLOAD)
1159 SetWindowText (choose_inst_text, "Select packages to download ");
1160 else
1161 SetWindowText (choose_inst_text, "Select packages to install ");
1162 GetParentRect (h, frame, &r);
1163 r.top += 2;
1164 r.bottom -= 2;
1165 create_listview (h, &r);
1166 #if 0
1167 load_dialog (h);
1168 #endif
1169 return TRUE;
1170 case WM_COMMAND:
1171 return HANDLE_WM_COMMAND (h, wParam, lParam, dialog_cmd);
1172 }
1173 return FALSE;
1174 }
1175
1176 /* Find out where to put existing tar file in local directory in
1177 known package array. */
1178 static void
1179 scan2 (char *path, unsigned int size)
1180 {
1181 packagemeta *pkg;
1182 fileparse f;
1183
1184 if (!parse_filename (path, f))
1185 return;
1186
1187 if (f.what[0] != '\0' && f.what[0] != 's')
1188 return;
1189
1190 packagedb db;
1191 pkg = db.packages.getbykey (f.pkg);
1192 if (pkg == NULL)
1193 return;
1194
1195 /* Scan existing package list looking for a match between a known
1196 package and a tar archive on disk.
1197 While scanning, keep track of appropriate "holes" in the trust
1198 table where a tar file could be put if no known entry
1199 exists.
1200
1201 We have 4 specific insertion points and one generic point.
1202 The generic point is in versioned order in the package version array.
1203 The specific points are
1204 *installed
1205 *prev
1206 *curr
1207 *exp.
1208
1209 if the version number matches a version in the db,
1210 we simply add this as a mirror source to that version.
1211 If it matches no version, we add a new version to the db.
1212
1213 Lastly if the version number does not matche one of installed/prev/current/exp
1214 AND we had to create a new version entry
1215 we apply the following heuristic:
1216 if there is no exp, we link this in exp.
1217 If there is an exp and this is higher, we link this in exp, and
1218 if there is no curr, bump what was in exp to curr. If there was a curr, we leave it be.
1219 if this is lower than exp, and there is no curr, link as curr. If there is a curr, leave it be.
1220 If this is lower than curr, and there is no prev, link as prev, if there is a prev, leave it be.
1221
1222 Whilst this logic is potentially wrong from time to time, it guarantees that
1223 setup.ini defined stability won't be altered unintentially. An alternative is to
1224 mark setup.ini defined prev/curr/exp packages as such, when this algorithm, can
1225 get smarter.
1226
1227 So, if setup.ini knows that ash-20010425-1.tar.gz is the current
1228 version and there is an ash-20010426-1.tar.gz in the current directory,
1229 the 20010426 version will be placed in the "test" slot, assuming that
1230 there is no test version listed in setup.ini. */
1231
1232 int added = 0;
1233 for (size_t n = 1; n <= pkg->versions.number (); n++)
1234 {
1235 if (!strcasecmp (f.ver, pkg->versions[n]->Canonical_version ()))
1236 {
1237 /* FIXME: Add a mirror entry */
1238 added = 1;
1239 }
1240 }
1241 if (!added)
1242 {
1243 /* FIXME: Add a new version */
1244
1245 /* And now the hole finder */
1246 #if 0
1247 if (!pkg->exp)
1248 pkg->exp = thenewver;
1249 else if (strcasecmp (f.ver, pkg->versions[n]->Canonicalversion ()) < 0)
1250 /* try curr */
1251 if (!pkg->curr)
1252 pkg->curr = thenewver;
1253 else if (strcasecmp (f.ver, pkg->versions[n]->Canonicalversion ()) <
1254 0)
1255 /* try prev */
1256 if (!pkg->prev)
1257 pkg->prev = thenewver;
1258 #endif
1259 }
1260
1261 }
1262
1263 static void
1264 scan_downloaded_files ()
1265 {
1266 find (".", scan2);
1267 }
1268
1269 void
1270 do_choose (HINSTANCE h)
1271 {
1272 int rv;
1273
1274 nextbutton = 0;
1275 bm_spin = LoadImage (h, MAKEINTRESOURCE (IDB_SPIN), IMAGE_BITMAP, 0, 0, 0);
1276 bm_rtarrow = LoadImage (h, MAKEINTRESOURCE (IDB_RTARROW), IMAGE_BITMAP,
1277 0, 0, 0);
1278
1279 bm_checkyes = LoadImage (h, MAKEINTRESOURCE (IDB_CHECK_YES), IMAGE_BITMAP,
1280 0, 0, 0);
1281 bm_checkno = LoadImage (h, MAKEINTRESOURCE (IDB_CHECK_NO), IMAGE_BITMAP,
1282 0, 0, 0);
1283 bm_checkna = LoadImage (h, MAKEINTRESOURCE (IDB_CHECK_NA), IMAGE_BITMAP,
1284 0, 0, 0);
1285
1286 register_windows (h);
1287
1288 if (source == IDC_SOURCE_DOWNLOAD || source == IDC_SOURCE_CWD)
1289 scan_downloaded_files ();
1290
1291 set_existence ();
1292 fill_missing_category ();
1293
1294 rv = DialogBox (h, MAKEINTRESOURCE (IDD_CHOOSE), 0, dialog_proc);
1295 if (rv == -1)
1296 fatal (IDS_DIALOG_FAILED);
1297
1298 log (LOG_BABBLE, "Chooser results...");
1299 packagedb db;
1300 for (size_t n = 1; n < db.packages.number (); n++)
1301 {
1302 packagemeta & pkg = *db.packages[n];
1303 // static const char *infos[] = { "nada", "prev", "curr", "test" };
1304 const char *trust = ((pkg.desired == pkg.prev) ? "prev"
1305 : (pkg.desired == pkg.curr) ? "curr"
1306 : (pkg.desired == pkg.exp) ? "test" : "unknown");
1307 const char *action = pkg.action_caption ();
1308 const char *installed =
1309 pkg.installed ? pkg.installed->Canonical_version () : "none";
1310
1311 log (LOG_BABBLE, "[%s] action=%s trust=%s installed=%s"
1312 " src?=%s",
1313 pkg.name, action, trust, installed,
1314 pkg.desired && pkg.desired->srcpicked ? "yes" : "no");
1315 if (pkg.Categories.number ())
1316 {
1317 /* List categories the package belongs to */
1318 int categories_len = 0;
1319 for (size_t n = 1; n <= pkg.Categories.number (); n++)
1320 categories_len += strlen (pkg.Categories[n]->key.name) + 2;
1321
1322 if (categories_len > 0)
1323 {
1324 char *categories = (char *) malloc (categories_len);
1325 strcpy (categories, pkg.Categories[1]->key.name);
1326 for (size_t n = 2; n <= pkg.Categories.number (); n++)
1327 {
1328 strcat (categories, ", ");
1329 strcat (categories, pkg.Categories[n]->key.name);
1330 }
1331 log (LOG_BABBLE, " categories=%s", categories);
1332 free (categories);
1333 }
1334 }
1335 if (pkg.desired && pkg.desired->required)
1336 {
1337 /* List other packages this package depends on */
1338 int requires_len = 0;
1339 Dependency *dp;
1340 for (dp = pkg.desired->required; dp; dp = dp->next)
1341 if (dp->package)
1342 requires_len += strlen (dp->package) + 2;
1343
1344 if (requires_len > 0)
1345 {
1346 char *requires = (char *) malloc (requires_len);
1347 strcpy (requires, pkg.desired->required->package);
1348 for (dp = pkg.desired->required->next; dp; dp = dp->next)
1349 if (dp->package)
1350 {
1351 strcat (requires, ", ");
1352 strcat (requires, dp->package);
1353 }
1354 log (LOG_BABBLE, " requires=%s", requires);
1355 free (requires);
1356 }
1357 }
1358 #if 0
1359
1360 /* FIXME: Reinstate this code, but spit out all mirror sites */
1361
1362 for (int t = 1; t < NTRUST; t++)
1363 {
1364 if (pkg->info[t].install)
1365 log (LOG_BABBLE, " [%s] ver=%s\n"
1366 " inst=%s %d exists=%s\n"
1367 " src=%s %d exists=%s",
1368 infos[t],
1369 pkg->info[t].version ? : "(none)",
1370 pkg->info[t].install ? : "(none)",
1371 pkg->info[t].install_size,
1372 (pkg->info[t].install_exists) ? "yes" : "no",
1373 pkg->info[t].source ? : "(none)",
1374 pkg->info[t].source_size,
1375 (pkg->info[t].source_exists) ? "yes" : "no");
1376 }
1377 #endif
1378 }
1379 }
This page took 0.090986 seconds and 5 git commands to generate.