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"
62 extern ThreeBarProgressPage Progress
;
64 static int initialized
= 0;
66 static HWND lv
, choose_inst_text
;
67 static PickView
*chooser
= NULL
;
69 static void set_view_mode (HWND h
, PickView::views mode
);
78 hdc
= BeginPaint (hwnd
, &ps
);
80 SelectObject (hdc
, chooser
->sysfont
);
81 SetBkColor (hdc
, GetSysColor (COLOR_WINDOW
));
82 SetTextColor (hdc
, GetSysColor (COLOR_WINDOWTEXT
));
85 GetClientRect (hwnd
, &cr
);
87 x
= cr
.left
- chooser
->scroll_ulc_x
;
88 y
= cr
.top
- chooser
->scroll_ulc_y
+ chooser
->header_height
;
90 IntersectClipRect (hdc
, cr
.left
, cr
.top
+ chooser
->header_height
, cr
.right
,
93 chooser
->contents
.paint (hdc
, x
, y
, 0, (chooser
->get_view_mode () ==
94 PickView::views::Category
) ? 0 : 1);
96 if (chooser
->contents
.itemcount () == 0)
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
));
104 EndPaint (hwnd
, &ps
);
107 static LRESULT CALLBACK
108 list_vscroll (HWND hwnd
, HWND hctl
, UINT code
, int pos
)
110 chooser
->scroll (hwnd
, SB_VERT
, &chooser
->scroll_ulc_y
, code
);
114 static LRESULT CALLBACK
115 list_hscroll (HWND hwnd
, HWND hctl
, UINT code
, int pos
)
117 chooser
->scroll (hwnd
, SB_HORZ
, &chooser
->scroll_ulc_x
, code
);
121 static LRESULT CALLBACK
122 list_click (HWND hwnd
, BOOL dblclk
, int x
, int y
, UINT hitCode
)
126 if (chooser
->contents
.itemcount () == 0)
129 if (y
< chooser
->header_height
)
131 x
+= chooser
->scroll_ulc_x
;
132 y
+= chooser
->scroll_ulc_y
- chooser
->header_height
;
134 row
= (y
+ ROW_MARGIN
/ 2) / chooser
->row_height
;
136 if (row
< 0 || row
>= chooser
->contents
.itemcount ())
139 refresh
= chooser
->click (row
, x
);
141 // XXX we need a method to queryt he database to see if more
142 // than just one package has changed! Until then...
148 GetClientRect (lv
, &r
);
150 memset (&si
, 0, sizeof (si
));
151 si
.cbSize
= sizeof (si
);
152 si
.fMask
= SIF_ALL
; /* SIF_RANGE was giving strange behaviour */
155 si
.nMax
= chooser
->contents
.itemcount () * chooser
->row_height
;
156 si
.nPage
= r
.bottom
- chooser
->header_height
;
158 /* if we are under the minimum display count ,
159 * set the offset to 0
161 if ((unsigned int) si
.nMax
<= si
.nPage
)
162 chooser
->scroll_ulc_y
= 0;
163 si
.nPos
= chooser
->scroll_ulc_y
;
165 SetScrollInfo (lv
, SB_VERT
, &si
, TRUE
);
167 InvalidateRect (lv
, &r
, TRUE
);
174 chooser
->headers
[chooser
->new_col
].x
- chooser
->scroll_ulc_x
;
176 chooser
->headers
[chooser
->src_col
+ 1].x
- chooser
->scroll_ulc_x
;
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
);
187 static LRESULT CALLBACK
188 listview_proc (HWND hwnd
, UINT message
, WPARAM wParam
, LPARAM lParam
)
193 return HANDLE_WM_HSCROLL (hwnd
, wParam
, lParam
, list_hscroll
);
195 return HANDLE_WM_VSCROLL (hwnd
, wParam
, lParam
, list_vscroll
);
197 return HANDLE_WM_LBUTTONDOWN (hwnd
, wParam
, lParam
, list_click
);
203 // pnmh = (LPNMHDR) lParam
204 LPNMHEADER phdr
= (LPNMHEADER
) lParam
;
205 switch (phdr
->hdr
.code
)
207 case HDN_ITEMCHANGED
:
208 if (phdr
->hdr
.hwndFrom
== chooser
->ListHeader ())
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
;
216 GetClientRect (hwnd
, &r
);
218 si
.cbSize
= sizeof (si
);
220 GetScrollInfo (hwnd
, SB_HORZ
, &si
);
221 int oldMax
= 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
;
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
,
240 return DefWindowProc (hwnd
, message
, wParam
, lParam
);
245 register_windows (HINSTANCE hinst
)
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";
264 RegisterClassEx (&wcex
);
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. */
275 while (n
<= db
.packages
.number ())
277 packagemeta
& pkg
= *db
.packages
[n
];
278 if (!pkg
.installed
&& !pkg
.accessible() &&
279 !pkg
.sourceAccessible() )
281 packagemeta
*pkgm
= db
.packages
.removebyindex (n
);
288 /* remove any source packages which are not accessible */
289 vector
<packagemeta
*>::iterator i
= db
.sourcePackages
.begin();
290 while (i
!= db
.sourcePackages
.end())
292 packagemeta
& pkg
= **i
;
293 if (!packageAccessible (pkg
))
295 packagemeta
*pkgm
= *i
;
297 i
= db
.sourcePackages
.erase (i
);
306 fill_missing_category ()
309 for (size_t n
= 1; n
<= db
.packages
.number (); n
++)
311 packagemeta
& pkg
= *db
.packages
[n
];
312 if (!pkg
.categories
.size ())
313 pkg
.add_category ("Misc");
314 pkg
.add_category ("All");
319 default_trust (HWND h
, trusts trust
)
321 chooser
->deftrust
= trust
;
323 for (size_t n
= 1; n
<= db
.packages
.number (); n
++)
325 packagemeta
& pkg
= *db
.packages
[n
];
327 || pkg
.categories
.find ("Base") != pkg
.categories
.end ()
328 || pkg
.categories
.find ("Misc") != pkg
.categories
.end ())
330 pkg
.desired
= pkg
.trustp (trust
);
332 pkg
.desired
.pick (pkg
.desired
.accessible()
333 && pkg
.desired
!= pkg
.installed
);
336 pkg
.desired
= packageversion ();
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())
346 log (LOG_BABBLE
) << "Removing empty category " << n
->first
<< endLog
;
347 packagedb::categories
.erase (n
++);
352 set_view_mode (HWND h
, PickView::views mode
)
354 chooser
->set_view_mode (mode
);
356 chooser
->clear_view ();
358 if (chooser
->get_view_mode () == PickView::views::Package
)
360 for (size_t n
= 1; n
<= db
.packages
.number (); n
++)
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
);
369 else if (chooser
->get_view_mode () == PickView::views::PackageFull
)
371 for (size_t n
= 1; n
<= db
.packages
.number (); n
++)
373 packagemeta
& pkg
= *db
.packages
[n
];
374 chooser
->insert_pkg (pkg
);
377 else if (chooser
->get_view_mode () == PickView::views::Category
)
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
);
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
);
407 create_listview (HWND dlg
, RECT
* r
)
409 lv
= CreateWindowEx (WS_EX_CLIENTEDGE
,
412 WS_CHILD
| WS_HSCROLL
| WS_VSCROLL
| WS_VISIBLE
,
414 r
->right
- r
->left
+ 1, r
->bottom
- r
->top
+ 1,
416 (HMENU
) MAKEINTRESOURCE (IDC_CHOOSE_LIST
),
418 ShowWindow (lv
, SW_SHOW
);
421 new PickView (PickView::views::Category
, lv
,
422 *db
.categories
.find("All"));
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
++)
431 packagemeta
& pkg
= *db
.packages
[n
];
432 pkg
.set_requirements (chooser
->deftrust
);
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
);
440 GetParentRect (HWND parent
, HWND child
, RECT
* r
)
443 GetWindowRect (child
, r
);
446 ScreenToClient (parent
, &p
);
451 ScreenToClient (parent
, &p
);
457 scanAVersion (packageversion version
)
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
465 if (!check_for_cached (*(version
.source())) && source
== IDC_SOURCE_CWD
)
466 version
.source()->sites
.clear();
470 scan_downloaded_files ()
472 /* Look at every known package, in all the known mirror dirs,
473 * and fill in the Cached attribute if it exists.
476 for (size_t n
= 1; n
<= db
.packages
.number (); ++n
)
478 packagemeta
& pkg
= *db
.packages
[n
];
479 for (set
<packageversion
>::iterator i
= pkg
.versions
.begin ();
480 i
!= pkg
.versions
.end (); ++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
489 if (!i
->accessible() && !pkgsrcver
.accessible()
490 && *i
!= pkg
.installed
)
493 pkg
.prev
= packageversion();
495 pkg
.curr
= packageversion();
497 pkg
.exp
= packageversion();
498 pkg
.versions
.erase(i
);
499 /* For now, leave the source version alone */
503 /* Don't explicity iterate through sources - any sources that aren't
504 referenced are unselectable anyway
509 ChooserPage::Create ()
511 return PropertyPage::Create (IDD_CHOOSE
);
515 ChooserPage::OnInit ()
520 register_windows (GetInstance ());
522 if (source
== IDC_SOURCE_DOWNLOAD
|| source
== IDC_SOURCE_CWD
)
523 scan_downloaded_files ();
526 fill_missing_category ();
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 ");
533 ::SetWindowText (choose_inst_text
, "Select packages to install ");
534 GetParentRect (GetHWND (), frame
, &r
);
537 create_listview (GetHWND (), &r
);
541 ChooserPage::OnNext ()
543 if (source
== IDC_SOURCE_CWD
)
546 Progress
.SetActivateTask (WM_APP_START_INSTALL
);
550 // Next, start download from internet
551 Progress
.SetActivateTask (WM_APP_START_DOWNLOAD
);
554 log (LOG_BABBLE
) << "Chooser results..." << endLog
;
556 for (size_t n
= 1; n
<= db
.packages
.number (); n
++)
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";
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 ())
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
++);
577 log (LOG_BABBLE
) << " categories=" << all_categories
<< endLog
;
580 if (pkg
.desired
.required())
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 ();
588 log (LOG_BABBLE
) << " requires=" << requires
;
593 /* FIXME: Reinstate this code, but spit out all mirror sites */
595 for (int t
= 1; t
< NTRUST
; t
++)
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",
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");
616 ChooserPage::OnBack ()
619 if (source
== IDC_SOURCE_CWD
)
620 return IDD_LOCAL_DIR
;
626 ChooserPage::OnMessageCmd (int id
, HWND hwndctl
, UINT code
)
628 if (code
!= BN_CLICKED
)
630 // Not a click notification, we don't care.
637 case IDC_CHOOSE_PREV
:
638 if (IsDlgButtonChecked (GetHWND (), id
))
640 default_trust (lv
, TRUST_PREV
);
641 for (size_t n
= 1; n
<= db
.packages
.number (); n
++)
643 packagemeta
& pkg
= *db
.packages
[n
];
644 pkg
.set_requirements (TRUST_PREV
);
646 set_view_mode (lv
, chooser
->get_view_mode ());
651 case IDC_CHOOSE_CURR
:
652 if (IsDlgButtonChecked (GetHWND (), id
))
654 default_trust (lv
, TRUST_CURR
);
655 for (size_t n
= 1; n
<= db
.packages
.number (); n
++)
657 packagemeta
& pkg
= *db
.packages
[n
];
658 pkg
.set_requirements (TRUST_CURR
);
660 set_view_mode (lv
, chooser
->get_view_mode ());
666 if (IsDlgButtonChecked (GetHWND (), id
))
668 default_trust (lv
, TRUST_TEST
);
669 for (size_t n
= 1; n
<= db
.packages
.number (); n
++)
671 packagemeta
& pkg
= *db
.packages
[n
];
672 pkg
.set_requirements (TRUST_TEST
);
674 set_view_mode (lv
, chooser
->get_view_mode ());
679 case IDC_CHOOSE_VIEW
:
680 set_view_mode (lv
, ++chooser
->get_view_mode ());
682 (GetHWND (), IDC_CHOOSE_VIEWCAPTION
, chooser
->mode_caption ()))
683 log (LOG_BABBLE
) << "Failed to set View button caption " <<
684 GetLastError () << endLog
;
689 // Wasn't recognized or handled.
693 // Was handled since we never got to default above.