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
=
44 #include "LogSingleton.h"
45 #include "filemanip.h"
46 #include "io_stream.h"
47 #include "propsheet.h"
51 #include "package_db.h"
52 #include "package_meta.h"
53 #include "package_version.h"
64 extern ThreeBarProgressPage Progress
;
66 static int initialized
= 0;
68 static HWND lv
, choose_inst_text
;
69 static PickView
*chooser
= NULL
;
71 static void set_view_mode (HWND h
, PickView::views mode
);
80 hdc
= BeginPaint (hwnd
, &ps
);
82 SelectObject (hdc
, chooser
->sysfont
);
83 SetBkColor (hdc
, GetSysColor (COLOR_WINDOW
));
84 SetTextColor (hdc
, GetSysColor (COLOR_WINDOWTEXT
));
87 GetClientRect (hwnd
, &cr
);
89 x
= cr
.left
- chooser
->scroll_ulc_x
;
90 y
= cr
.top
- chooser
->scroll_ulc_y
+ chooser
->header_height
;
92 IntersectClipRect (hdc
, cr
.left
, cr
.top
+ chooser
->header_height
, cr
.right
,
95 chooser
->contents
.paint (hdc
, x
, y
, 0, (chooser
->get_view_mode () ==
96 PickView::views::Category
) ? 0 : 1);
98 if (chooser
->contents
.itemcount () == 0)
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
));
106 EndPaint (hwnd
, &ps
);
109 static LRESULT CALLBACK
110 list_vscroll (HWND hwnd
, HWND hctl
, UINT code
, int pos
)
112 chooser
->scroll (hwnd
, SB_VERT
, &chooser
->scroll_ulc_y
, code
);
116 static LRESULT CALLBACK
117 list_hscroll (HWND hwnd
, HWND hctl
, UINT code
, int pos
)
119 chooser
->scroll (hwnd
, SB_HORZ
, &chooser
->scroll_ulc_x
, code
);
123 static LRESULT CALLBACK
124 list_click (HWND hwnd
, BOOL dblclk
, int x
, int y
, UINT hitCode
)
128 if (chooser
->contents
.itemcount () == 0)
131 if (y
< chooser
->header_height
)
133 x
+= chooser
->scroll_ulc_x
;
134 y
+= chooser
->scroll_ulc_y
- chooser
->header_height
;
136 row
= (y
+ ROW_MARGIN
/ 2) / chooser
->row_height
;
138 if (row
< 0 || row
>= chooser
->contents
.itemcount ())
141 refresh
= chooser
->click (row
, x
);
143 // XXX we need a method to queryt he database to see if more
144 // than just one package has changed! Until then...
150 GetClientRect (lv
, &r
);
152 memset (&si
, 0, sizeof (si
));
153 si
.cbSize
= sizeof (si
);
154 si
.fMask
= SIF_ALL
; /* SIF_RANGE was giving strange behaviour */
157 si
.nMax
= chooser
->contents
.itemcount () * chooser
->row_height
;
158 si
.nPage
= r
.bottom
- chooser
->header_height
;
160 /* if we are under the minimum display count ,
161 * set the offset to 0
163 if ((unsigned int) si
.nMax
<= si
.nPage
)
164 chooser
->scroll_ulc_y
= 0;
165 si
.nPos
= chooser
->scroll_ulc_y
;
167 SetScrollInfo (lv
, SB_VERT
, &si
, TRUE
);
169 InvalidateRect (lv
, &r
, TRUE
);
176 chooser
->headers
[chooser
->new_col
].x
- chooser
->scroll_ulc_x
;
178 chooser
->headers
[chooser
->src_col
+ 1].x
- chooser
->scroll_ulc_x
;
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
);
189 static LRESULT CALLBACK
190 listview_proc (HWND hwnd
, UINT message
, WPARAM wParam
, LPARAM lParam
)
195 return HANDLE_WM_HSCROLL (hwnd
, wParam
, lParam
, list_hscroll
);
197 return HANDLE_WM_VSCROLL (hwnd
, wParam
, lParam
, list_vscroll
);
199 return HANDLE_WM_LBUTTONDOWN (hwnd
, wParam
, lParam
, list_click
);
205 // pnmh = (LPNMHDR) lParam
206 LPNMHEADER phdr
= (LPNMHEADER
) lParam
;
207 switch (phdr
->hdr
.code
)
209 case HDN_ITEMCHANGED
:
210 if (phdr
->hdr
.hwndFrom
== chooser
->ListHeader ())
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
;
218 GetClientRect (hwnd
, &r
);
220 si
.cbSize
= sizeof (si
);
222 GetScrollInfo (hwnd
, SB_HORZ
, &si
);
223 int oldMax
= 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
;
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
,
242 return DefWindowProc (hwnd
, message
, wParam
, lParam
);
247 register_windows (HINSTANCE hinst
)
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";
266 RegisterClassEx (&wcex
);
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 ())
279 packagemeta
& pkg
= **i
;
280 if (!pkg
.installed
&& !pkg
.accessible() &&
281 !pkg
.sourceAccessible() )
283 packagemeta
*pkgm
= *i
;
285 i
= db
.packages
.erase (i
);
291 /* remove any source packages which are not accessible */
292 vector
<packagemeta
*>::iterator i
= db
.sourcePackages
.begin();
293 while (i
!= db
.sourcePackages
.end())
295 packagemeta
& pkg
= **i
;
296 if (!packageAccessible (pkg
))
298 packagemeta
*pkgm
= *i
;
300 i
= db
.sourcePackages
.erase (i
);
309 fill_missing_category ()
312 for (vector
<packagemeta
*>::iterator i
= db
.packages
.begin ();
313 i
!= db
.packages
.end (); ++i
)
315 packagemeta
& pkg
= **i
;
316 if (!pkg
.categories
.size ())
317 pkg
.add_category ("Misc");
318 pkg
.add_category ("All");
323 default_trust (HWND h
, trusts trust
)
325 chooser
->deftrust
= trust
;
327 for (vector
<packagemeta
*>::iterator i
= db
.packages
.begin ();
328 i
!= db
.packages
.end (); ++i
)
330 packagemeta
& pkg
= **i
;
332 || pkg
.categories
.find ("Base") != pkg
.categories
.end ()
333 || pkg
.categories
.find ("Misc") != pkg
.categories
.end ())
335 pkg
.desired
= pkg
.trustp (trust
);
337 pkg
.desired
.pick (pkg
.desired
.accessible()
338 && pkg
.desired
!= pkg
.installed
);
341 pkg
.desired
= packageversion ();
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())
351 log (LOG_BABBLE
) << "Removing empty category " << n
->first
<< endLog
;
352 packagedb::categories
.erase (n
++);
357 set_view_mode (HWND h
, PickView::views mode
)
359 chooser
->set_view_mode (mode
);
361 chooser
->clear_view ();
363 if (chooser
->get_view_mode () == PickView::views::Package
)
365 for (vector
<packagemeta
*>::iterator i
= db
.packages
.begin ();
366 i
!= db
.packages
.end (); ++i
)
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
);
375 else if (chooser
->get_view_mode () == PickView::views::PackageFull
)
377 for (vector
<packagemeta
*>::iterator i
= db
.packages
.begin ();
378 i
!= db
.packages
.end (); ++i
)
379 chooser
->insert_pkg (**i
);
381 else if (chooser
->get_view_mode () == PickView::views::Category
)
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
);
391 GetClientRect (h
, &r
);
393 memset (&si
, 0, sizeof (si
));
394 si
.cbSize
= sizeof (si
);
397 si
.nMax
= chooser
->headers
[chooser
->last_col
].x
+ chooser
->headers
[chooser
->last_col
].width
; // + HMARGIN;
399 SetScrollInfo (h
, SB_HORZ
, &si
, TRUE
);
401 si
.nMax
= chooser
->contents
.itemcount () * chooser
->row_height
;
402 si
.nPage
= r
.bottom
- chooser
->header_height
;
403 SetScrollInfo (h
, SB_VERT
, &si
, TRUE
);
405 chooser
->scroll_ulc_x
= chooser
->scroll_ulc_y
= 0;
407 InvalidateRect (h
, &r
, TRUE
);
411 create_listview (HWND dlg
, RECT
* r
)
413 lv
= CreateWindowEx (WS_EX_CLIENTEDGE
,
416 WS_CHILD
| WS_HSCROLL
| WS_VSCROLL
| WS_VISIBLE
,
418 r
->right
- r
->left
+ 1, r
->bottom
- r
->top
+ 1,
420 (HMENU
) MAKEINTRESOURCE (IDC_CHOOSE_LIST
),
422 ShowWindow (lv
, SW_SHOW
);
425 new PickView (PickView::views::Category
, lv
,
426 *db
.categories
.find("All"));
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
)
436 packagemeta
& pkg
= **i
;
437 pkg
.set_requirements (chooser
->deftrust
);
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
);
445 GetParentRect (HWND parent
, HWND child
, RECT
* r
)
448 GetWindowRect (child
, r
);
451 ScreenToClient (parent
, &p
);
456 ScreenToClient (parent
, &p
);
462 scanAVersion (packageversion version
)
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
470 if (!check_for_cached (*(version
.source())) && source
== IDC_SOURCE_CWD
)
471 version
.source()->sites
.clear();
475 scan_downloaded_files ()
477 /* Look at every known package, in all the known mirror dirs,
478 * and fill in the Cached attribute if it exists.
481 for (vector
<packagemeta
*>::iterator n
= db
.packages
.begin ();
482 n
!= db
.packages
.end (); ++n
)
484 packagemeta
& pkg
= **n
;
485 for (set
<packageversion
>::iterator i
= pkg
.versions
.begin ();
486 i
!= pkg
.versions
.end (); ++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
495 if (!i
->accessible() && !pkgsrcver
.accessible()
496 && *i
!= pkg
.installed
)
499 pkg
.prev
= packageversion();
501 pkg
.curr
= packageversion();
503 pkg
.exp
= packageversion();
504 pkg
.versions
.erase(i
);
505 /* For now, leave the source version alone */
509 /* Don't explicity iterate through sources - any sources that aren't
510 referenced are unselectable anyway
515 ChooserPage::Create ()
517 return PropertyPage::Create (IDD_CHOOSE
);
521 ChooserPage::OnInit ()
526 register_windows (GetInstance ());
528 if (source
== IDC_SOURCE_DOWNLOAD
|| source
== IDC_SOURCE_CWD
)
529 scan_downloaded_files ();
532 fill_missing_category ();
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 ");
539 ::SetWindowText (choose_inst_text
, "Select packages to install ");
540 GetParentRect (GetHWND (), frame
, &r
);
543 create_listview (GetHWND (), &r
);
547 ChooserPage::OnNext ()
549 if (source
== IDC_SOURCE_CWD
)
552 Progress
.SetActivateTask (WM_APP_START_INSTALL
);
556 // Next, start download from internet
557 Progress
.SetActivateTask (WM_APP_START_DOWNLOAD
);
560 log (LOG_BABBLE
) << "Chooser results..." << endLog
;
562 for (vector
<packagemeta
*>::iterator i
= db
.packages
.begin ();
563 i
!= db
.packages
.end (); ++i
)
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";
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 ())
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
++);
583 log (LOG_BABBLE
) << " categories=" << all_categories
<< endLog
;
586 if (pkg
.desired
.required())
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 ();
594 log (LOG_BABBLE
) << " requires=" << requires
;
597 pkg
.logAllVersions();
603 ChooserPage::OnBack ()
606 if (source
== IDC_SOURCE_CWD
)
607 return IDD_LOCAL_DIR
;
613 ChooserPage::OnMessageCmd (int id
, HWND hwndctl
, UINT code
)
615 if (code
!= BN_CLICKED
)
617 // Not a click notification, we don't care.
624 case IDC_CHOOSE_PREV
:
625 if (IsDlgButtonChecked (GetHWND (), id
))
627 default_trust (lv
, TRUST_PREV
);
628 for (vector
<packagemeta
*>::iterator i
= db
.packages
.begin ();
629 i
!= db
.packages
.end (); ++i
)
631 packagemeta
& pkg
= **i
;
632 pkg
.set_requirements (TRUST_PREV
);
634 set_view_mode (lv
, chooser
->get_view_mode ());
639 case IDC_CHOOSE_CURR
:
640 if (IsDlgButtonChecked (GetHWND (), id
))
642 default_trust (lv
, TRUST_CURR
);
643 for (vector
<packagemeta
*>::iterator i
= db
.packages
.begin ();
644 i
!= db
.packages
.end (); ++i
)
646 packagemeta
& pkg
= **i
;
647 pkg
.set_requirements (TRUST_CURR
);
649 set_view_mode (lv
, chooser
->get_view_mode ());
655 if (IsDlgButtonChecked (GetHWND (), id
))
657 default_trust (lv
, TRUST_TEST
);
658 for (vector
<packagemeta
*>::iterator i
= db
.packages
.begin ();
659 i
!= db
.packages
.end (); ++i
)
661 packagemeta
& pkg
= **i
;
662 pkg
.set_requirements (TRUST_TEST
);
664 set_view_mode (lv
, chooser
->get_view_mode ());
669 case IDC_CHOOSE_VIEW
:
670 set_view_mode (lv
, ++chooser
->get_view_mode ());
672 (GetHWND (), IDC_CHOOSE_VIEWCAPTION
, chooser
->mode_caption ()))
673 log (LOG_BABBLE
) << "Failed to set View button caption " <<
674 GetLastError () << endLog
;
679 // Wasn't recognized or handled.
683 // Was handled since we never got to default above.