2 * Copyright (c) 2000, 2001 Red Hat, Inc.
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.
9 * A copy of the GNU General Public License can be found at
12 * Written by DJ Delorie <dj@cygnus.com>
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
29 static const char *cvsid
=
48 #include "filemanip.h"
49 #include "io_stream.h"
50 #include "propsheet.h"
54 #include "package_db.h"
55 #include "package_meta.h"
56 #include "package_version.h"
57 #include "cygpackage.h"
63 extern ThreeBarProgressPage Progress
;
65 static int initialized
= 0;
67 static HWND lv
, nextbutton
, choose_inst_text
;
68 static PickView
*chooser
= NULL
;
70 static void set_view_mode (HWND h
, PickView::views mode
);
79 hdc
= BeginPaint (hwnd
, &ps
);
81 SelectObject (hdc
, chooser
->sysfont
);
82 SetBkColor (hdc
, GetSysColor (COLOR_WINDOW
));
83 SetTextColor (hdc
, GetSysColor (COLOR_WINDOWTEXT
));
86 GetClientRect (hwnd
, &cr
);
88 x
= cr
.left
- chooser
->scroll_ulc_x
;
89 y
= cr
.top
- chooser
->scroll_ulc_y
+ chooser
->header_height
;
91 IntersectClipRect (hdc
, cr
.left
, cr
.top
+ chooser
->header_height
, cr
.right
,
94 chooser
->contents
.paint (hdc
, x
, y
, 0, (chooser
->get_view_mode () ==
95 PickView::views::Category
) ? 0 : 1);
97 if (chooser
->contents
.itemcount () == 0)
99 static const char *msg
= "Nothing to Install/Update";
100 if (source
== IDC_SOURCE_DOWNLOAD
)
101 msg
= "Nothing to Download";
102 TextOut (hdc
, HMARGIN
, chooser
->header_height
, msg
, strlen (msg
));
105 EndPaint (hwnd
, &ps
);
108 static LRESULT CALLBACK
109 list_vscroll (HWND hwnd
, HWND hctl
, UINT code
, int pos
)
111 chooser
->scroll (hwnd
, SB_VERT
, &chooser
->scroll_ulc_y
, code
);
115 static LRESULT CALLBACK
116 list_hscroll (HWND hwnd
, HWND hctl
, UINT code
, int pos
)
118 chooser
->scroll (hwnd
, SB_HORZ
, &chooser
->scroll_ulc_x
, code
);
122 static LRESULT CALLBACK
123 list_click (HWND hwnd
, BOOL dblclk
, int x
, int y
, UINT hitCode
)
127 if (chooser
->contents
.itemcount () == 0)
130 if (y
< chooser
->header_height
)
132 x
+= chooser
->scroll_ulc_x
;
133 y
+= chooser
->scroll_ulc_y
- chooser
->header_height
;
135 row
= (y
+ ROW_MARGIN
/ 2) / chooser
->row_height
;
137 if (row
< 0 || row
>= chooser
->contents
.itemcount ())
140 refresh
= chooser
->click (row
, x
);
142 // XXX we need a method to queryt he database to see if more
143 // than just one package has changed! Until then...
149 GetClientRect (lv
, &r
);
151 memset (&si
, 0, sizeof (si
));
152 si
.cbSize
= sizeof (si
);
153 si
.fMask
= SIF_ALL
; /* SIF_RANGE was giving strange behaviour */
156 si
.nMax
= chooser
->contents
.itemcount () * chooser
->row_height
;
157 si
.nPage
= r
.bottom
- chooser
->header_height
;
159 /* if we are under the minimum display count ,
160 * set the offset to 0
162 if ((unsigned int) si
.nMax
<= si
.nPage
)
163 chooser
->scroll_ulc_y
= 0;
164 si
.nPos
= chooser
->scroll_ulc_y
;
166 SetScrollInfo (lv
, SB_VERT
, &si
, TRUE
);
168 InvalidateRect (lv
, &r
, TRUE
);
175 chooser
->headers
[chooser
->new_col
].x
- chooser
->scroll_ulc_x
;
177 chooser
->headers
[chooser
->src_col
+ 1].x
- chooser
->scroll_ulc_x
;
179 chooser
->header_height
+ row
* chooser
->row_height
-
180 chooser
->scroll_ulc_y
;
181 rect
.bottom
= rect
.top
+ chooser
->row_height
;
182 InvalidateRect (hwnd
, &rect
, TRUE
);
188 static LRESULT CALLBACK
189 listview_proc (HWND hwnd
, UINT message
, WPARAM wParam
, LPARAM lParam
)
194 return HANDLE_WM_HSCROLL (hwnd
, wParam
, lParam
, list_hscroll
);
196 return HANDLE_WM_VSCROLL (hwnd
, wParam
, lParam
, list_vscroll
);
198 return HANDLE_WM_LBUTTONDOWN (hwnd
, wParam
, lParam
, list_click
);
204 // pnmh = (LPNMHDR) lParam
205 LPNMHEADER phdr
= (LPNMHEADER
) lParam
;
206 switch (phdr
->hdr
.code
)
208 case HDN_ITEMCHANGED
:
209 if (phdr
->hdr
.hwndFrom
== chooser
->ListHeader ())
211 if (phdr
->pitem
&& phdr
->pitem
->mask
& HDI_WIDTH
)
212 chooser
->headers
[phdr
->iItem
].width
= phdr
->pitem
->cxy
;
213 for (int i
= 1; i
<= chooser
->last_col
; i
++)
214 chooser
->headers
[i
].x
=
215 chooser
->headers
[i
- 1].x
+ chooser
->headers
[i
- 1].width
;
217 GetClientRect (hwnd
, &r
);
219 si
.cbSize
= sizeof (si
);
221 GetScrollInfo (hwnd
, SB_HORZ
, &si
);
222 int oldMax
= si
.nMax
;
224 chooser
->headers
[chooser
->last_col
].x
+
225 chooser
->headers
[chooser
->last_col
].width
;
226 if (si
.nTrackPos
&& oldMax
> si
.nMax
)
227 si
.nTrackPos
+= si
.nMax
- oldMax
;
229 SetScrollInfo (hwnd
, SB_HORZ
, &si
, TRUE
);
230 InvalidateRect (hwnd
, &r
, TRUE
);
231 if (si
.nTrackPos
&& oldMax
> si
.nMax
)
232 chooser
->scroll (hwnd
, SB_HORZ
, &chooser
->scroll_ulc_x
,
241 return DefWindowProc (hwnd
, message
, wParam
, lParam
);
246 register_windows (HINSTANCE hinst
)
255 memset (&wcex
, 0, sizeof (wcex
));
256 wcex
.cbSize
= sizeof (WNDCLASSEX
);
257 wcex
.style
= CS_HREDRAW
| CS_VREDRAW
;
258 wcex
.lpfnWndProc
= listview_proc
;
259 wcex
.hInstance
= hinst
;
260 wcex
.hIcon
= LoadIcon (0, IDI_APPLICATION
);
261 wcex
.hCursor
= LoadCursor (0, IDC_ARROW
);
262 wcex
.hbrBackground
= (HBRUSH
) (COLOR_WINDOW
+ 1);
263 wcex
.lpszClassName
= "listview";
265 RegisterClassEx (&wcex
);
272 /* Remove packages that are in the db, not installed, and have no
275 while (n
<= db
.packages
.number ())
277 packagemeta
& pkg
= *db
.packages
[n
];
278 bool mirrors
= false;
280 while (o
<= pkg
.versions
.number () && !mirrors
)
282 packageversion
& ver
= *pkg
.versions
[o
];
283 if (((source
!= IDC_SOURCE_CWD
) && (ver
.bin
.sites
.number ()
284 || ver
.src
.sites
.number ()))
285 || ver
.bin
.Cached () || ver
.src
.Cached ())
289 if (!pkg
.installed
&& !mirrors
)
291 packagemeta
*pkgm
= db
.packages
.removebyindex (n
);
300 fill_missing_category ()
303 for (size_t n
= 1; n
<= db
.packages
.number (); n
++)
305 packagemeta
& pkg
= *db
.packages
[n
];
306 if (!pkg
.Categories
.number ())
307 pkg
.add_category (db
.categories
.registerbykey ("Misc"));
308 pkg
.add_category (db
.categories
.registerbykey ("All"));
313 default_trust (HWND h
, trusts trust
)
315 chooser
->deftrust
= trust
;
317 for (size_t n
= 1; n
<= db
.packages
.number (); n
++)
319 packagemeta
& pkg
= *db
.packages
[n
];
321 || pkg
.Categories
.getbykey (db
.categories
.registerbykey ("Base"))
322 || pkg
.Categories
.getbykey (db
.categories
.registerbykey ("Misc")))
324 pkg
.desired
= pkg
.trustp (trust
);
327 pkg
.desired
->binpicked
= pkg
.desired
== pkg
.installed
? 0 : 1;
328 pkg
.desired
->srcpicked
= 0;
335 GetClientRect (h
, &r
);
336 InvalidateRect (h
, &r
, TRUE
);
338 SetFocus (nextbutton
);
339 // and then do the same for categories with no packages.
341 while (n
<= db
.categories
.number ())
343 if (!db
.categories
[n
]->packages
)
345 Category
*cat
= db
.categories
.removebyindex (n
);
354 set_view_mode (HWND h
, PickView::views mode
)
356 chooser
->set_view_mode (mode
);
358 chooser
->clear_view ();
360 if (chooser
->get_view_mode () == PickView::views::Package
)
362 for (size_t n
= 1; n
<= db
.packages
.number (); n
++)
364 packagemeta
& pkg
= *db
.packages
[n
];
365 if ((!pkg
.desired
&& pkg
.installed
)
367 && (pkg
.desired
->srcpicked
|| pkg
.desired
->binpicked
)))
368 chooser
->insert_pkg (pkg
);
371 else if (chooser
->get_view_mode () == PickView::views::PackageFull
)
373 for (size_t n
= 1; n
<= db
.packages
.number (); n
++)
375 packagemeta
& pkg
= *db
.packages
[n
];
376 chooser
->insert_pkg (pkg
);
379 else if (chooser
->get_view_mode () == PickView::views::Category
)
381 /* start collapsed. TODO: make this a chooser flag */
382 for (size_t n
= 1; n
<= db
.categories
.number (); n
++)
383 chooser
->insert_category (db
.categories
[n
], CATEGORY_COLLAPSED
);
387 GetClientRect (h
, &r
);
389 memset (&si
, 0, sizeof (si
));
390 si
.cbSize
= sizeof (si
);
393 si
.nMax
= chooser
->headers
[chooser
->last_col
].x
+ chooser
->headers
[chooser
->last_col
].width
; // + HMARGIN;
395 SetScrollInfo (h
, SB_HORZ
, &si
, TRUE
);
397 si
.nMax
= chooser
->contents
.itemcount () * chooser
->row_height
;
398 si
.nPage
= r
.bottom
- chooser
->header_height
;
399 SetScrollInfo (h
, SB_VERT
, &si
, TRUE
);
401 chooser
->scroll_ulc_x
= chooser
->scroll_ulc_y
= 0;
403 InvalidateRect (h
, &r
, TRUE
);
406 SetFocus (nextbutton
);
410 create_listview (HWND dlg
, RECT
* r
)
412 lv
= CreateWindowEx (WS_EX_CLIENTEDGE
,
415 WS_CHILD
| WS_HSCROLL
| WS_VSCROLL
| WS_VISIBLE
,
417 r
->right
- r
->left
+ 1, r
->bottom
- r
->top
+ 1,
419 (HMENU
) MAKEINTRESOURCE (IDC_CHOOSE_LIST
),
421 ShowWindow (lv
, SW_SHOW
);
424 new PickView (PickView::views::Category
, lv
,
425 db
.categories
.registerbykey ("All"));
427 default_trust (lv
, TRUST_CURR
);
428 set_view_mode (lv
, PickView::views::Category
);
429 if (!SetDlgItemText (dlg
, IDC_CHOOSE_VIEWCAPTION
, chooser
->mode_caption ()))
430 log (LOG_BABBLE
, "Failed to set View button caption %ld",
432 for (size_t n
= 1; n
<= db
.packages
.number (); n
++)
434 packagemeta
& pkg
= *db
.packages
[n
];
435 pkg
.set_requirements (chooser
->deftrust
);
437 /* FIXME: do we need to init the desired fields ? */
438 static int ta
[] = { IDC_CHOOSE_CURR
, 0 };
439 rbset (dlg
, ta
, IDC_CHOOSE_CURR
);
444 dialog_cmd (HWND h
, int id
, HWND hwndctl
, UINT code
)
449 case IDC_CHOOSE_PREV
:
450 default_trust (lv
, TRUST_PREV
);
451 for (size_t n
= 1; n
<= db
.packages
.number (); n
++)
453 packagemeta
& pkg
= *db
.packages
[n
];
454 pkg
.set_requirements (TRUST_PREV
);
456 set_view_mode (lv
, chooser
->get_view_mode ());
458 case IDC_CHOOSE_CURR
:
459 default_trust (lv
, TRUST_CURR
);
460 for (size_t n
= 1; n
<= db
.packages
.number (); n
++)
462 packagemeta
& pkg
= *db
.packages
[n
];
463 pkg
.set_requirements (TRUST_CURR
);
465 set_view_mode (lv
, chooser
->get_view_mode ());
468 default_trust (lv
, TRUST_TEST
);
469 for (size_t n
= 1; n
<= db
.packages
.number (); n
++)
471 packagemeta
& pkg
= *db
.packages
[n
];
472 pkg
.set_requirements (TRUST_TEST
);
474 set_view_mode (lv
, chooser
->get_view_mode ());
476 case IDC_CHOOSE_VIEW
:
477 set_view_mode (lv
, ++chooser
->get_view_mode ());
479 (h
, IDC_CHOOSE_VIEWCAPTION
, chooser
->mode_caption ()))
480 log (LOG_BABBLE
, "Failed to set View button caption %ld",
485 if (source
== IDC_SOURCE_CWD
)
486 NEXT (IDD_S_INSTALL
);
488 NEXT (IDD_S_DOWNLOAD
);
493 if (source
== IDC_SOURCE_CWD
)
494 NEXT (IDD_LOCAL_DIR
);
507 GetParentRect (HWND parent
, HWND child
, RECT
* r
)
510 GetWindowRect (child
, r
);
513 ScreenToClient (parent
, &p
);
518 ScreenToClient (parent
, &p
);
524 dialog_proc (HWND h
, UINT message
, WPARAM wParam
, LPARAM lParam
)
531 nextbutton
= GetDlgItem (h
, IDOK
);
532 frame
= GetDlgItem (h
, IDC_LISTVIEW_POS
);
533 choose_inst_text
= GetDlgItem (h
, IDC_CHOOSE_INST_TEXT
);
534 if (source
== IDC_SOURCE_DOWNLOAD
)
535 SetWindowText (choose_inst_text
, "Select packages to download ");
537 SetWindowText (choose_inst_text
, "Select packages to install ");
538 GetParentRect (h
, frame
, &r
);
541 create_listview (h
, &r
);
548 return HANDLE_WM_COMMAND (h
, wParam
, lParam
, dialog_cmd
);
553 /* Find out where to put existing tar file in local directory in
554 known package array. */
556 scan2 (char *path
, unsigned int size
)
561 if (!parse_filename (path
, f
))
565 // only process source package files?
566 if (f
.what
.size () != 0 && f
.what
.cstr_oneuse ()[0] != 's')
571 pkg
= db
.packages
.getbykey (f
.pkg
);
575 /* Scan existing package list looking for a match between a known
576 package and a tar archive on disk.
577 While scanning, keep track of appropriate "holes" in the trust
578 table where a tar file could be put if no known entry
581 We have 4 specific insertion points and one generic point.
582 The generic point is in versioned order in the package version array.
583 The specific points are
589 if the version number matches a version in the db,
590 we simply add this as a mirror source to that version.
591 If it matches no version, we add a new version to the db.
593 Lastly if the version number does not matche one of installed/prev/current/exp
594 AND we had to create a new version entry
595 we apply the following heuristic:
596 if there is no exp, we link this in exp.
597 If there is an exp and this is higher, we link this in exp, and
598 if there is no curr, bump what was in exp to curr. If there was a curr, we leave it be.
599 if this is lower than exp, and there is no curr, link as curr. If there is a curr, leave it be.
600 If this is lower than curr, and there is no prev, link as prev, if there is a prev, leave it be.
602 Whilst this logic is potentially wrong from time to time, it guarantees that
603 setup.ini defined stability won't be altered unintentially. An alternative is to
604 mark setup.ini defined prev/curr/exp packages as such, when this algorithm, can
607 So, if setup.ini knows that ash-20010425-1.tar.gz is the current
608 version and there is an ash-20010426-1.tar.gz in the current directory,
609 the 20010426 version will be placed in the "test" slot, assuming that
610 there is no test version listed in setup.ini. */
613 for (size_t n
= 1; n
<= pkg
->versions
.number (); n
++)
615 if (!f
.ver
.casecompare (pkg
->versions
[n
]->Canonical_version ()))
617 if (f
.what
== String ())
620 pkg
->versions
[n
]->bin
.set_cached (String ("file://") + path
);
622 else if (f
.what
== "src")
625 pkg
->versions
[n
]->src
.set_cached (String ("file://") + path
);
633 // Do we want old versions to show up
634 packageversion
*pv
= new cygpackage (f
.pkg
);
635 ((cygpackage
*) pv
)->set_canonical_version (f
.ver
);
637 pv
->bin
.set_cached (String ("file://") + path
);
639 // patch or src, assume src until someone complains
640 pv
->src
.set_cached (String ("file://") + path
);
641 pkg
->add_version (*pv
);
645 /* And now the hole finder */
648 pkg
->exp
= thenewver
;
649 else if (strcasecmp (f
.ver
, pkg
->versions
[n
]->Canonicalversion ()) < 0)
652 pkg
->curr
= thenewver
;
653 else if (strcasecmp (f
.ver
, pkg
->versions
[n
]->Canonicalversion ()) <
657 pkg
->prev
= thenewver
;
664 scan_downloaded_files ()
670 do_choose (HINSTANCE h
, HWND owner
)
676 register_windows (h
);
678 if (source
== IDC_SOURCE_DOWNLOAD
|| source
== IDC_SOURCE_CWD
)
679 scan_downloaded_files ();
682 fill_missing_category ();
684 rv
= DialogBox (h
, MAKEINTRESOURCE (IDD_CHOOSE
), owner
, dialog_proc
);
686 fatal (owner
, IDS_DIALOG_FAILED
);
688 log (LOG_BABBLE
, "Chooser results...");
690 for (size_t n
= 1; n
<= db
.packages
.number (); n
++)
692 packagemeta
& pkg
= *db
.packages
[n
];
693 // static const char *infos[] = { "nada", "prev", "curr", "test" };
694 const char *trust
= ((pkg
.desired
== pkg
.prev
) ? "prev"
695 : (pkg
.desired
== pkg
.curr
) ? "curr"
696 : (pkg
.desired
== pkg
.exp
) ? "test" : "unknown");
697 String action
= pkg
.action_caption ();
698 String
const installed
=
699 pkg
.installed
? pkg
.installed
->Canonical_version () : "none";
701 log (LOG_BABBLE
, "[%s] action=%s trust=%s installed=%s"
703 pkg
.name
.cstr_oneuse (), action
.cstr_oneuse (), trust
,
704 installed
.cstr_oneuse (), pkg
.desired
705 && pkg
.desired
->srcpicked
? "yes" : "no");
706 if (pkg
.Categories
.number ())
708 /* List categories the package belongs to */
709 String all_categories
= pkg
.Categories
[1]->key
.name
;
710 for (size_t n
= 2; n
<= pkg
.Categories
.number (); n
++)
711 all_categories
+= String (", ") + pkg
.Categories
[n
]->key
.name
;
713 log (LOG_BABBLE
, String (" categories=") + all_categories
);
715 if (pkg
.desired
&& pkg
.desired
->required
)
717 /* List other packages this package depends on */
718 Dependency
*dp
= pkg
.desired
->required
;
719 String requires
= dp
->package
;
720 for (dp
= dp
->next
; dp
; dp
= dp
->next
)
721 if (dp
->package
.size ())
722 requires
+= String (", ") + dp
->package
;
724 log (LOG_BABBLE
, String (" requires=") + requires
);
728 /* FIXME: Reinstate this code, but spit out all mirror sites */
730 for (int t
= 1; t
< NTRUST
; t
++)
732 if (pkg
->info
[t
].install
)
733 log (LOG_BABBLE
, " [%s] ver=%s\n"
734 " inst=%s %d exists=%s\n"
735 " src=%s %d exists=%s",
737 pkg
->info
[t
].version
? : "(none)",
738 pkg
->info
[t
].install
? : "(none)",
739 pkg
->info
[t
].install_size
,
740 (pkg
->info
[t
].install_exists
) ? "yes" : "no",
741 pkg
->info
[t
].source
? : "(none)",
742 pkg
->info
[t
].source_size
,
743 (pkg
->info
[t
].source_exists
) ? "yes" : "no");
749 #define WM_APP_START_CHOOSE WM_APP+0
750 #define WM_APP_CHOOSE_IS_FINISHED WM_APP+1
752 extern void do_choose (HINSTANCE h
, HWND owner
);
755 do_choose_thread (void *p
)
759 cp
= static_cast < ChooserPage
* >(p
);
761 do_choose (cp
->GetInstance (), cp
->GetHWND ());
763 cp
->PostMessage (WM_APP_CHOOSE_IS_FINISHED
);
768 bool ChooserPage::Create ()
770 return PropertyPage::Create (IDD_CHOOSER
);
774 ChooserPage::OnActivate ()
776 GetOwner ()->SetButtons (0);
777 PostMessage (WM_APP_START_CHOOSE
);
780 bool ChooserPage::OnMessageApp (UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
784 case WM_APP_START_CHOOSE
:
786 // Start the chooser thread.
787 _beginthread (do_choose_thread
, 0, this);
790 case WM_APP_CHOOSE_IS_FINISHED
:
797 GetOwner ()->PressButton (PSBTN_CANCEL
);
804 GetOwner ()->SetActivePageByID (next_dialog
);
809 // Next, start download from internet
810 Progress
.SetActivateTask (WM_APP_START_DOWNLOAD
);
811 GetOwner ()->SetActivePageByID (IDD_INSTATUS
);
817 Progress
.SetActivateTask (WM_APP_START_INSTALL
);
818 GetOwner ()->SetActivePageByID (IDD_INSTATUS
);