view netinstall/choose.cc @ 3767:6b2ef948e140

[xemacs-hg @ 2006-12-29 18:09:38 by aidan] etc/ChangeLog addition: 2006-12-21 Aidan Kehoe <kehoea@parhasard.net> * unicode/unicode-consortium/8859-7.TXT: Update the mapping to the 2003 version of ISO 8859-7. lisp/ChangeLog addition: 2006-12-21 Aidan Kehoe <kehoea@parhasard.net> * mule/cyrillic.el: * mule/cyrillic.el (iso-8859-5): * mule/cyrillic.el (cyrillic-koi8-r-encode-table): Add syntax, case support for Cyrillic; make some parentheses more Lispy. * mule/european.el: Content moved to latin.el, file deleted. * mule/general-late.el: If Unicode tables are to be loaded at dump time, do it here, not in loadup.el. * mule/greek.el: Add syntax, case support for Greek. * mule/latin.el: Move the content of european.el here. Change the case table mappings to use hexadecimal codes, to make cross reference to the standards easier. In all cases, take character syntax from similar characters in Latin-1 , rather than deciding separately what syntax they should take. Add (incomplete) support for case with Turkish. Remove description of the character sets used from the language environments' doc strings, since now that we create variant language environments on the fly, such descriptions will often be inaccurate. Set the native-coding-system language info property while setting the other coding-system properties of the language. * mule/misc-lang.el (ipa): Remove the language environment. The International Phonetic _Alphabet_ is not a language, it's inane to have a corresponding language environment in XEmacs. * mule/mule-cmds.el (create-variant-language-environment): Also modify the coding-priority when creating a new language environment; document that. * mule/mule-cmds.el (get-language-environment-from-locale): Recognise that the 'native-coding-system language-info property can be a list, interpret it correctly when it is one. 2006-12-21 Aidan Kehoe <kehoea@parhasard.net> * coding.el (coding-system-category): Use the new 'unicode-type property for finding what sort of Unicode coding system subtype a coding system is, instead of the overshadowed 'type property. * dumped-lisp.el (preloaded-file-list): mule/european.el has been removed. * loadup.el (really-early-error-handler): Unicode tables loaded at dump time are now in mule/general-late.el. * simple.el (count-lines): Add some backslashes to to parentheses in docstrings to help fontification along. * simple.el (what-cursor-position): Wrap a line to fit in 80 characters. * unicode.el: Use the 'unicode-type property, not 'type, for setting the Unicode coding-system subtype. src/ChangeLog addition: 2006-12-21 Aidan Kehoe <kehoea@parhasard.net> * file-coding.c: Update the make-coding-system docstring to reflect unicode-type * general-slots.h: New symbol, unicode-type, since 'type was being overridden when accessing a coding system's Unicode subtype. * intl-win32.c: Backslash a few parentheses, to help fontification along. * intl-win32.c (complex_vars_of_intl_win32): Use the 'unicode-type symbol, not 'type, when creating the Microsoft Unicode coding system. * unicode.c (unicode_putprop): * unicode.c (unicode_getprop): * unicode.c (unicode_print): Using 'type as the property name when working out what Unicode subtype a given coding system is was broken, since there's a general coding system property called 'type. Change the former to use 'unicode-type instead.
author aidan
date Fri, 29 Dec 2006 18:09:51 +0000
parents ce0b3f2eff35
children
line wrap: on
line source

/*
 * Copyright (c) 2000, Red Hat, Inc.
 *
 *     This program is free software; you can redistribute it and/or modify
 *     it under the terms of the GNU General Public License as published by
 *     the Free Software Foundation; either version 2 of the License, or
 *     (at your option) any later version.
 *
 *     A copy of the GNU General Public License can be found at
 *     http://www.gnu.org/
 *
 * Written by DJ Delorie <dj@cygnus.com>
 *
 */

/* The purpose of this file is to let the user choose which packages
   to install, and which versions of the package when more than one
   version is provided.  The "trust" level serves as an indication as
   to which version should be the default choice.  At the moment, all
   we do is compare with previously installed packages to skip any
   that are already installed (by setting the action to ACTION_SAME).
   While the "trust" stuff is supported, it's not really implemented
   yet.  We always prefer the "current" option.  In the future, this
   file might have a user dialog added to let the user choose to not
   install packages, or to install packages that aren't installed by
   default. */

#include "win32.h"
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

#include "dialog.h"
#include "resource.h"
#include "state.h"
#include "ini.h"
#include "concat.h"
#include "msg.h"
#include "log.h"
#include "find.h"
#include "reginfo.h"

#define HMARGIN		10
#define ROW_MARGIN	5
#define ICON_MARGIN	4

#define CHECK_SIZE	11

#define TRUST_KEEP	101
#define TRUST_UNINSTALL	102
#define TRUST_NONE	103

static int initialized = 0;

static int full_list = 0;

static int scroll_ulc_x, scroll_ulc_y;

static HWND lv, nextbutton;
static TEXTMETRIC tm;
static int header_height;
static HANDLE sysfont;
static int row_height;
static HANDLE bm_spin, bm_rtarrow, bm_checkyes, bm_checkno, bm_checkna;
static HDC bitmap_dc;

static struct {
  char *text;
  int slen;
  int width;
  int x;
} headers[] = {
  { "Current", 7, 0, 0 },
#define CURRENT_COL 0
  { "New", 3, 0, 0 },
#define NEW_COL 1
  { "Src?", 4, 0, 0 },
#define SRC_COL 2
  { "Package", 7, 0, 0 },
#define PACKAGE_COL 3
  { 0, 0, 0, 0 }
};
#define NUM_COLUMNS (sizeof(headers)/(sizeof(headers[0]))-1)

int *package_indexes, nindexes;

struct ExtraPackageInfo {
  char *installed_file;	/* filename of previous "install" file */
  char *installed_ver;	/* version part */
  int   installed_size; /* ditto, size. */

  int in_partial_list;
  int pick;
  int npick;
  int which_is_installed; /* == TRUST* or -1 */

  struct {
    int src_avail;
    int trust;		/* may be keep or uninstall */
    char *caption;	/* ==0 at EOL */
  } chooser[NTRUST+3];	/* one extra for NULL above */
};

static ExtraPackageInfo *extra;

static void
paint (HWND hwnd)
{
  HDC hdc;
  PAINTSTRUCT ps;
  int x, y, i, ii;

  hdc = BeginPaint (hwnd, &ps);

  SelectObject (hdc, sysfont);

  RECT cr;
  GetClientRect (hwnd, &cr);

  POINT p;

  x = cr.left - scroll_ulc_x;
  y = cr.top - scroll_ulc_y + header_height;


  for (i=0; headers[i].text; i++)
    {
      TextOut (hdc, x+headers[i].x, 3, headers[i].text, headers[i].slen);
      MoveToEx (hdc, x+headers[i].x, header_height-3, &p);
      LineTo (hdc, x+headers[i].x+headers[i].width, header_height-3);
    }

  IntersectClipRect (hdc, cr.left, cr.top+header_height, cr.right, cr.bottom);

  for (ii=0; ii<nindexes; ii++)
    {
      i = package_indexes[ii];
      int r = y + ii * row_height;
      int by = r + tm.tmHeight - 11;
      if (extra[i].installed_ver && extra[i].installed_ver[0])
	{
	  TextOut (hdc, x+headers[CURRENT_COL].x, r,
		   extra[i].installed_ver, strlen (extra[i].installed_ver));
	  SelectObject (bitmap_dc, bm_rtarrow);
	  BitBlt (hdc, x+headers[CURRENT_COL].x+headers[0].width+ICON_MARGIN/2+HMARGIN/2, by,
		  11, 11, bitmap_dc, 0, 0, SRCCOPY);
	}

      char *s = extra[i].chooser[extra[i].pick].caption;
      if (s)
	{
	  TextOut (hdc, x+headers[NEW_COL].x + 11 + ICON_MARGIN, r,
		   s, strlen (s));
	  if (extra[i].npick > 1)
	    {
	      SelectObject (bitmap_dc, bm_spin);
	      BitBlt (hdc, x+headers[NEW_COL].x, by, 11, 11,
		      bitmap_dc, 0, 0, SRCCOPY);
	    }
	}

      HANDLE check_bm = bm_checkna;
      if (extra[i].chooser[extra[i].pick].src_avail)
	{
	  if (package[i].srcaction == SRCACTION_NO)
	    check_bm = bm_checkno;
	  else if (package[i].srcaction == SRCACTION_YES)
	    check_bm = bm_checkyes;
	}
      SelectObject (bitmap_dc, check_bm);
      BitBlt (hdc, x+headers[SRC_COL].x, by, 11, 11,
	      bitmap_dc, 0, 0, SRCCOPY);

      if (package[i].name)
	TextOut (hdc, x+headers[PACKAGE_COL].x, r, package[i].name, strlen(package[i].name));
    }

  if (nindexes == 0)
    {
      static char *m = "Nothing to Install/Update";
      TextOut (hdc, HMARGIN, header_height, m, strlen (m));
    }
  
  EndPaint (hwnd, &ps);
}

static void
scroll_common (HWND hwnd, int which, int *var, int code)
{
  SCROLLINFO si;
  si.cbSize = sizeof (si);
  si.fMask = SIF_ALL;
  GetScrollInfo (hwnd, which, &si);

  switch (code)
    {
    case SB_THUMBTRACK:
      si.nPos = si.nTrackPos;
      break;
    case SB_THUMBPOSITION:
      break;
    case SB_BOTTOM:
      si.nPos = si.nMax;
      break;
    case SB_TOP:
      si.nPos = 0;
      break;
    case SB_LINEDOWN:
      si.nPos += row_height;
      break;
    case SB_LINEUP:
      si.nPos -= row_height;
      break;
    case SB_PAGEDOWN:
      si.nPos += si.nPage * 9/10;
      break;
    case SB_PAGEUP:
      si.nPos -= si.nPage * 9/10;
      break;
    }

  if ((int)si.nPos < 0)
    si.nPos = 0;
  if ((int)(si.nPos + si.nPage) > si.nMax)
    si.nPos = si.nMax - si.nPage;

  si.fMask = SIF_POS;
  SetScrollInfo (hwnd, which, &si, TRUE);

  int ox = scroll_ulc_x;
  int oy = scroll_ulc_y;
  *var = si.nPos;

  RECT cr, sr;
  GetClientRect (hwnd, &cr);
  sr = cr;
  sr.top += header_height;
  ScrollWindow (hwnd, ox - scroll_ulc_x, oy - scroll_ulc_y, &sr, &sr);
  sr.bottom = sr.top;
  sr.top = cr.top;
  ScrollWindow (hwnd, ox - scroll_ulc_x, 0, &sr, &sr);
}

static LRESULT CALLBACK
list_vscroll (HWND hwnd, HWND hctl, UINT code, int pos)
{
  scroll_common (hwnd, SB_VERT, &scroll_ulc_y, code);
  return FALSE;
}

static LRESULT CALLBACK
list_hscroll (HWND hwnd, HWND hctl, UINT code, int pos)
{
  scroll_common (hwnd, SB_HORZ, &scroll_ulc_x, code);
  return FALSE;
}

static LRESULT CALLBACK
list_click (HWND hwnd, BOOL dblclk, int x, int y, UINT hitCode)
{
  int r;

  if (nindexes == 0)
    return 0;

  if (y < header_height)
    return 0;
  x += scroll_ulc_x;
  y += scroll_ulc_y - header_height;

  r = (y + ROW_MARGIN/2) / row_height;

  if (r < 0 || r >= npackages)
    return 0;

  int p = package_indexes[r];

  if (x >= headers[NEW_COL].x - HMARGIN/2 && x <= headers[NEW_COL+1].x - HMARGIN/2)
    {
      extra[p].pick ++;
      if (extra[p].chooser[extra[p].pick].caption == 0)
	extra[p].pick = 0;
    }

  if (x >= headers[SRC_COL].x - HMARGIN/2 && x <= headers[SRC_COL+1].x - HMARGIN/2)
    {
      if (extra[p].chooser[extra[p].pick].src_avail)
	package[p].srcaction ^= (SRCACTION_NO^SRCACTION_YES);
    }

  RECT rect;
  rect.left = headers[NEW_COL].x - scroll_ulc_x;
  rect.right = headers[SRC_COL+1].x - scroll_ulc_x;
  rect.top = header_height + r * row_height - scroll_ulc_y;
  rect.bottom = rect.top + row_height;
  InvalidateRect (hwnd, &rect, TRUE);
  return FALSE;
}

static LRESULT CALLBACK
listview_proc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
  switch (message) {
  case WM_HSCROLL:
    return HANDLE_WM_HSCROLL (hwnd, wParam, lParam, list_hscroll);
  case WM_VSCROLL:
    return HANDLE_WM_VSCROLL (hwnd, wParam, lParam, list_vscroll);
  case WM_LBUTTONDOWN:
    return HANDLE_WM_LBUTTONDOWN (hwnd, wParam, lParam, list_click);
  case WM_PAINT:
    paint (hwnd);
    return 0;
  default:
    return DefWindowProc (hwnd, message, wParam, lParam);
  }
}

static void
register_windows (HINSTANCE hinst)
{
  WNDCLASSEX wcex;
  static int done = 0;

  if (done)
    return;
  done = 1;

  memset (&wcex, 0, sizeof (wcex));
  wcex.cbSize = sizeof (WNDCLASSEX);
  wcex.style = CS_HREDRAW | CS_VREDRAW;
  wcex.lpfnWndProc = listview_proc;
  wcex.hInstance = hinst;
  wcex.hIcon = LoadIcon (0, IDI_APPLICATION);
  wcex.hCursor = LoadCursor (0, IDC_ARROW);
  wcex.hbrBackground = (HBRUSH) (COLOR_WINDOW+1);
  wcex.lpszClassName = "listview";

  RegisterClassEx (&wcex);
}

static void
note_width (HDC dc, char *string, int addend, int column)
{
  if (!string)
    return;
  SIZE s;
  GetTextExtentPoint32 (dc, string, strlen (string), &s);
  if (headers[column].width < s.cx + addend)
    headers[column].width = s.cx + addend;
}

static int
best_trust (int p, int trust)
{
  int t;
  for (t=trust; t>=0; t--)
    if (package[p].info[t].install)
      return t;
  for (t=trust+1; t<=NTRUST; t++)
    if (package[p].info[t].install)
      return t;
  if (extra[p].installed_file)
    return TRUST_KEEP;
  return TRUST_NONE;
}

static void
default_trust (HWND h, int trust)
{
  int i, t, c;

  for (i=0; i<npackages; i++)
    {
      t = best_trust (i, trust);
      extra[i].pick = 1;
      for (c=0; c<extra[i].npick; c++)
	if (t == extra[i].chooser[c].trust)
	  extra[i].pick = c;
      if (install_type == IDC_INSTALL_NATIVE 
	  && package[i].type == TY_CYGWIN
	  ||
	  install_type == IDC_INSTALL_CYGWIN 
	  && package[i].type == TY_NATIVE)
	extra[i].pick = extra[i].npick -1;
    }
  RECT r;
  GetClientRect (h, &r);
  InvalidateRect (h, &r, TRUE);
  if (nextbutton)
    SetFocus (nextbutton);
}

static void
set_full_list (HWND h, int isfull)
{
  int i, j;
  full_list = isfull;
  if (package_indexes == 0)
    package_indexes = (int *) malloc (npackages * sizeof (int));
  for (i=j=0; i<npackages; i++)
    {
      if (isfull || extra[i].in_partial_list)
	package_indexes[j++] = i;
    }
  nindexes = j;

  RECT r;
  GetClientRect (h, &r);
  SCROLLINFO si;
  memset (&si, 0, sizeof (si));
  si.cbSize = sizeof (si);
  si.fMask = SIF_ALL;
  si.nMin = 0;
  si.nMax = headers[2].x + headers[2].width + HMARGIN;
  si.nPage = r.right;
  SetScrollInfo (h, SB_HORZ, &si, TRUE);

  si.nMax = nindexes * row_height;
  si.nPage = r.bottom - header_height;
  SetScrollInfo (h, SB_VERT, &si, TRUE);

  scroll_ulc_x = scroll_ulc_y = 0;

  InvalidateRect (h, &r, TRUE);

  if (nextbutton)
    SetFocus (nextbutton);
}

static void
build_labels ()
{
  int i;
  for (i=0; i<npackages; i++)
    {
      int c = 0, t;

#define C extra[i].chooser[c]
      if (extra[i].installed_ver)
	{
	  C.caption = "Uninstall";
	  C.trust = TRUST_UNINSTALL;
	  c++;
	  C.caption = "Keep";
	  C.trust = TRUST_KEEP;
	  c++;
	}

      for (t=TRUST_PREV; t<NTRUST; t++)
	if (package[i].info[t].install)
	  if (t != extra[i].which_is_installed)
	    {
	      C.caption = package[i].info[t].version;
	      if (C.caption == 0 || C.caption[0] == 0)
		C.caption = "0.0";
	      C.trust = t;
	      if (package[i].info[t].source)
		C.src_avail = 1;
	      c++;
	      /* we intentionally skip TRUST_PREV */
	      if (t != TRUST_PREV || !extra[i].installed_ver)
		extra[i].in_partial_list = 1;

	    }

      if (c == 0)
	{
	  C.caption = "N/A";
	  C.trust = TRUST_NONE;
	  c++;
	}

      if (! extra[i].installed_file)
	{
	  C.caption = "Skip";
	  C.trust = TRUST_NONE;
	  c++;
	}

      C.caption = 0;
      extra[i].npick = c;
#undef C
    }
}

static void
create_listview (HWND dlg, RECT *r)
{
  int i, t;
  lv = CreateWindowEx (WS_EX_CLIENTEDGE,
		       "listview",
		       "listviewwindow",
		       WS_CHILD | WS_HSCROLL | WS_VSCROLL | WS_VISIBLE,
		       r->left, r->top,
		       r->right-r->left+1, r->bottom-r->top+1,
		       dlg,
		       NULL, // ??? MAKEINTRESOURCE(IDC_CHOOSE_LIST),
		       hinstance,
		       0);
  ShowWindow (lv, SW_SHOW);

  for (i=0; headers[i].text; i++)
    headers[i].width = 0;

  HDC dc = GetDC (lv);
  sysfont = GetStockObject (DEFAULT_GUI_FONT);
  SelectObject (dc, sysfont);
  GetTextMetrics (dc, &tm);
  header_height = tm.tmHeight + 5 + 3;

  bitmap_dc = CreateCompatibleDC (dc);

  row_height = (tm.tmHeight + tm.tmExternalLeading + ROW_MARGIN);
  int irh = tm.tmExternalLeading + tm.tmDescent + 11 + ROW_MARGIN;
  if (row_height < irh)
    row_height = irh;

  for (i=0; headers[i].text; i++)
    note_width (dc, headers[i].text, 0, i);
  for (i=0; i<npackages; i++)
    {
      note_width (dc, extra[i].installed_ver, 0, CURRENT_COL);
      note_width (dc, extra[i].installed_ver, 11+ICON_MARGIN, NEW_COL);
      for (t=0; t<NTRUST; t++)
	note_width (dc, package[i].info[t].version, 11+ICON_MARGIN, NEW_COL);
      note_width (dc, package[i].name, 0, PACKAGE_COL);
      note_width (dc, package[i].sdesc, 0, PACKAGE_COL);
    }
  note_width (dc, "keep", 11+ICON_MARGIN, NEW_COL);
  note_width (dc, "uninstall", 11+ICON_MARGIN, NEW_COL);

  headers[CURRENT_COL].x = HMARGIN/2;
  headers[NEW_COL].x = (headers[CURRENT_COL].x + headers[CURRENT_COL].width
			+ HMARGIN + 11 + ICON_MARGIN);
  headers[SRC_COL].x = headers[NEW_COL].x + headers[NEW_COL].width + HMARGIN;
  headers[PACKAGE_COL].x = headers[SRC_COL].x + headers[SRC_COL].width + HMARGIN;

  set_full_list (lv, full_list);
  default_trust (lv, TRUST_CURR);

  ReleaseDC (lv, dc);
}

static BOOL
dialog_cmd (HWND h, int id, HWND hwndctl, UINT code)
{
  switch (id)
    {

    case IDC_CHOOSE_PREV:
      default_trust (lv, TRUST_PREV);
      break;
    case IDC_CHOOSE_CURR:
      default_trust (lv, TRUST_CURR);
      break;
    case IDC_CHOOSE_EXP:
      default_trust (lv, TRUST_TEST);
      break;
    case IDC_CHOOSE_FULLPART:
      set_full_list (lv, !full_list);
      break;

    case IDOK:
      if (source == IDC_SOURCE_CWD)
	NEXT (IDD_S_INSTALL);
      else
	NEXT (IDD_S_DOWNLOAD);
      break;

    case IDC_BACK:
      initialized = 0;
      if (source == IDC_SOURCE_CWD)
	NEXT (IDD_ROOT);
      else
	NEXT (IDD_SITE);
      break;

    case IDCANCEL:
      NEXT (0);
      break;
    }
  return FALSE;
}

static void
GetParentRect (HWND parent, HWND child, RECT *r)
{
  POINT p;
  GetWindowRect (child, r);
  p.x = r->left;
  p.y = r->top;
  ScreenToClient (parent, &p);
  r->left = p.x;
  r->top = p.y;
  p.x = r->right;
  p.y = r->bottom;
  ScreenToClient (parent, &p);
  r->right = p.x;
  r->bottom = p.y;
}

static BOOL CALLBACK
dialog_proc (HWND h, UINT message, WPARAM wParam, LPARAM lParam)
{
  HWND frame;
  RECT r;
  switch (message)
    {
    case WM_INITDIALOG:
      nextbutton = GetDlgItem (h, IDOK);
      frame = GetDlgItem (h, IDC_LISTVIEW_POS);
      GetParentRect (h, frame, &r);
      r.top += 2;
      r.bottom -= 2;
      create_listview (h, &r);
#if 0
      load_dialog (h);
#endif
      return FALSE;
    case WM_COMMAND:
      return HANDLE_WM_COMMAND (h, wParam, lParam, dialog_cmd);
    }
  return FALSE;
}

char *
base (char *s)
{
  if (!s)
    return 0;
  char *rv = s;
  while (*s)
    {
      if ((*s == '/' || *s == ':' || *s == '\\') && s[1])
	rv = s+1;
      s++;
    }
  return rv;
}

static void
scan2 (char *path, unsigned int size)
{
  int i, t;
  for (i=0; i<npackages; i++)
    for (t=0; t<NTRUST; t++)
      if (package[i].info[t].install
	  && strcmp (base (package[i].info[t].install), base (path)) == 0
	  && package[i].info[t].install_size == (int)size)
	{
	  extra[i].installed_file = package[i].info[t].install;
	  extra[i].installed_size = size;
	  extra[i].which_is_installed = t;
	  extra[i].installed_ver = package[i].info[t].version;
	  if (!extra[i].installed_ver)
	    extra[i].installed_ver = "0";
	}
}

static void
scan_downloaded_files ()
{
  find (".", scan2);
}

static void
read_installed_db ()
{
  int i;
  if (!root_dir)
    return;

  char line[1000], pkg[1000], inst[1000], src[1000];
  int instsz, srcsz;

  FILE *db = fopen (concat (root_dir, XEMACS_SETUP_DIR, "installed.db", 0), "rt");
  if (!db)
    return;

  while (fgets (line, 1000, db))
    {
      src[0] = 0;
      srcsz = 0;
      sscanf (line, "%s %s %d %s %d", pkg, inst, &instsz, src, &srcsz);

      for (i=0; i<npackages; i++)
	if (strcmp (package[i].name, pkg) == 0)
	  {
	    int t;
	    extra[i].installed_file = inst;
	    extra[i].installed_size = instsz;

	    for (t=0; t<NTRUST; t++)
	      if (package[i].info[t].install
		  && strcmp (base (package[i].info[t].install), base (inst)) == 0)
		{
		  extra[i].which_is_installed = t;
		  extra[i].installed_ver = package[i].info[t].version;
		  break;
		}

	    if (extra[i].installed_ver == 0) /* still */
	      {
		char *v, *d;
		for (v=base (inst); *v; v++)
		  if (*v == '-' && isdigit(v[1]))
		    {
		      v++;
		      break;
		    }
		if (!v)
		  v = inst;
		for (d=v; *d; d++)
		  if (strncmp (d, ".tar", 4) == 0
		      || strncmp (d, "-pkg", 4) == 0)
		    {
		      *d = 0;
		      break;
		    }
		if (v[0])
		  extra[i].installed_ver = strdup (v);
		else
		  extra[i].installed_ver = "0";
	      }
	    break;
	  }
    }
  fclose (db);
}

int CDECL
package_sort (const void *va, const void *vb)
{
  Package *a = (Package *)va;
  Package *b = (Package *)vb;
  return strcmp (a->name, b->name);
}

void
do_choose (HINSTANCE h)
{
  int rv, i;

  qsort (package, npackages, sizeof (package[0]), package_sort);

  nextbutton = 0;
  bm_spin = LoadImage (h, MAKEINTRESOURCE (IDB_SPIN), IMAGE_BITMAP, 0, 0, 0);
  bm_rtarrow = LoadImage (h, MAKEINTRESOURCE (IDB_RTARROW), IMAGE_BITMAP, 0, 0, 0);

  bm_checkyes = LoadImage (h, MAKEINTRESOURCE (IDB_CHECK_YES), IMAGE_BITMAP, 0, 0, 0);
  bm_checkno = LoadImage (h, MAKEINTRESOURCE (IDB_CHECK_NO), IMAGE_BITMAP, 0, 0, 0);
  bm_checkna = LoadImage (h, MAKEINTRESOURCE (IDB_CHECK_NA), IMAGE_BITMAP, 0, 0, 0);

  extra = (ExtraPackageInfo *) malloc (npackages * sizeof (ExtraPackageInfo));
  memset (extra, 0, npackages * sizeof (ExtraPackageInfo));
  for (i=0; i<npackages; i++)
    extra[i].which_is_installed = -1;

  register_windows (h);

  if (source == IDC_SOURCE_DOWNLOAD)
    scan_downloaded_files ();
  else
    read_installed_db ();
  build_labels ();

  rv = DialogBox (h, MAKEINTRESOURCE (IDD_CHOOSE), 0, dialog_proc);
  if (rv == -1)
    fatal (IDS_DIALOG_FAILED);

  for (i=0; i<npackages; i++)
    {
      switch (extra[i].chooser[extra[i].pick].trust)
	{
	case TRUST_PREV:
	case TRUST_CURR:
	case TRUST_TEST:
	  if (extra[i].installed_file)
	    package[i].action = ACTION_UPGRADE;
	  else
	    package[i].action = ACTION_NEW;
	  package[i].trust = extra[i].chooser[extra[i].pick].trust;
	  // pick up the actual core package to install
	  if (package[i].type == TY_CYGWIN || package[i].type == TY_NATIVE
	      && xemacs_package == 0)
	    xemacs_package = &package[i];
	  break;

	case TRUST_UNINSTALL:
	  package[i].action = ACTION_UNINSTALL;
	  break;

	case TRUST_KEEP:
	case TRUST_NONE:
	default:
	  package[i].action = ACTION_SAME;
	  break;
	}
    }

  log (LOG_BABBLE, "Chooser results...");
  for (i=0; i<npackages; i++)
    {
      static char *infos[] = {"prev", "curr", "test"};
      const char *trust = ((package[i].trust == TRUST_PREV) ? "prev"
			   : (package[i].trust == TRUST_CURR) ? "curr"
			   : (package[i].trust == TRUST_TEST) ? "test"
			   : "unknown");
      const char *action = ((package[i].action == ACTION_UNKNOWN) ? "unknown"
			    : (package[i].action == ACTION_SAME) ? "same"
			    : (package[i].action == ACTION_NEW) ? "new"
			    : (package[i].action == ACTION_UPGRADE) ? "upgrade"
			    : (package[i].action == ACTION_UNINSTALL) ? "uninstall"
			    : (package[i].action == ACTION_ERROR) ? "error"
			    : "unknown");

      log (LOG_BABBLE, "[%s] action=%s trust=%s src? %s", package[i].name, action, trust,
	   package[i].srcaction == SRCACTION_NO ? "no" : "yes");
      for (int t=0; t<NTRUST; t++)
	{
	  if (package[i].info[t].install)
	    log (LOG_BABBLE, "[%s] ver %s inst %s %d src %s %d",
		 infos[t],
		 package[i].info[t].version ? package[i].info[t].version : "(none)",
		 package[i].info[t].install ? package[i].info[t].install : "(none)",
		 package[i].info[t].install_size,
		 package[i].info[t].source ? package[i].info[t].source : "(none)",
		 package[i].info[t].source_size);
	}
    }
}