view netinstall/choose.cc @ 826:6728e641994e

[xemacs-hg @ 2002-05-05 11:30:15 by ben] syntax cache, 8-bit-format, lots of code cleanup README.packages: Update info about --package-path. i.c: Create an inheritable event and pass it on to XEmacs, so that ^C can be handled properly. Intercept ^C and signal the event. "Stop Build" in VC++ now works. bytecomp-runtime.el: Doc string changes. compat.el: Some attempts to redo this to make it truly useful and fix the "multiple versions interacting with each other" problem. Not yet done. Currently doesn't work. files.el: Use with-obsolete-variable to avoid warnings in new revert-buffer code. xemacs.mak: Split up CFLAGS into a version without flags specifying the C library. The problem seems to be that minitar depends on zlib, which depends specifically on libc.lib, not on any of the other C libraries. Unless you compile with libc.lib, you get errors -- specifically, no _errno in the other libraries, which must make it something other than an int. (#### But this doesn't seem to obtain in XEmacs, which also uses zlib, and can be linked with any of the C libraries. Maybe zlib is used differently and doesn't need errno, or maybe XEmacs provides an int errno; ... I don't understand. Makefile.in.in: Fix so that packages are around when testing. abbrev.c, alloc.c, buffer.c, buffer.h, bytecode.c, callint.c, casefiddle.c, casetab.c, casetab.h, charset.h, chartab.c, chartab.h, cmds.c, console-msw.h, console-stream.c, console-x.c, console.c, console.h, data.c, device-msw.c, device.c, device.h, dialog-msw.c, dialog-x.c, dired-msw.c, dired.c, doc.c, doprnt.c, dumper.c, editfns.c, elhash.c, emacs.c, eval.c, event-Xt.c, event-gtk.c, event-msw.c, event-stream.c, events.c, events.h, extents.c, extents.h, faces.c, file-coding.c, file-coding.h, fileio.c, fns.c, font-lock.c, frame-gtk.c, frame-msw.c, frame-x.c, frame.c, frame.h, glade.c, glyphs-gtk.c, glyphs-msw.c, glyphs-msw.h, glyphs-x.c, glyphs.c, glyphs.h, gui-msw.c, gui-x.c, gui.h, gutter.h, hash.h, indent.c, insdel.c, intl-win32.c, intl.c, keymap.c, lisp-disunion.h, lisp-union.h, lisp.h, lread.c, lrecord.h, lstream.c, lstream.h, marker.c, menubar-gtk.c, menubar-msw.c, menubar-x.c, menubar.c, minibuf.c, mule-ccl.c, mule-charset.c, mule-coding.c, mule-wnnfns.c, nas.c, objects-msw.c, objects-x.c, opaque.c, postgresql.c, print.c, process-nt.c, process-unix.c, process.c, process.h, profile.c, rangetab.c, redisplay-gtk.c, redisplay-msw.c, redisplay-output.c, redisplay-x.c, redisplay.c, redisplay.h, regex.c, regex.h, scrollbar-msw.c, search.c, select-x.c, specifier.c, specifier.h, symbols.c, symsinit.h, syntax.c, syntax.h, syswindows.h, tests.c, text.c, text.h, tooltalk.c, ui-byhand.c, ui-gtk.c, unicode.c, win32.c, window.c: Another big Ben patch. -- FUNCTIONALITY CHANGES: add partial support for 8-bit-fixed, 16-bit-fixed, and 32-bit-fixed formats. not quite done yet. (in particular, needs functions to actually convert the buffer.) NOTE: lots of changes to regex.c here. also, many new *_fmt() inline funs that take an Internal_Format argument. redo syntax cache code. make the cache per-buffer; keep the cache valid across calls to functions that use it. also keep it valid across insertions/deletions and extent changes, as much as is possible. eliminate the junky regex-reentrancy code by passing in the relevant lisp info to the regex routines as local vars. add general mechanism in extents code for signalling extent changes. fix numerous problems with the case-table implementation; yoshiki never properly transferred many algorithms from old-style to new-style case tables. redo char tables to support a default argument, so that mapping only occurs over changed args. change many chartab functions to accept Lisp_Object instead of Lisp_Char_Table *. comment out the code in font-lock.c by default, because font-lock.el no longer uses it. we should consider eliminating it entirely. Don't output bell as ^G in console-stream when not a TTY. add -mswindows-termination-handle to interface with i.c, so we can properly kill a build. add more error-checking to buffer/string macros. add some additional buffer_or_string_() funs. -- INTERFACE CHANGES AFFECTING MORE CODE: switch the arguments of write_c_string and friends to be consistent with write_fmt_string, which must have printcharfun first. change BI_* macros to BYTE_* for increased clarity; similarly for bi_* local vars. change VOID_TO_LISP to be a one-argument function. eliminate no-longer-needed CVOID_TO_LISP. -- char/string macro changes: rename MAKE_CHAR() to make_emchar() for slightly less confusion with make_char(). (The former generates an Emchar, the latter a Lisp object. Conceivably we should rename make_char() -> wrap_char() and similarly for make_int(), make_float().) Similar changes for other *CHAR* macros -- we now consistently use names with `emchar' whenever we are working with Emchars. Any remaining name with just `char' always refers to a Lisp object. rename macros with XSTRING_* to string_* except for those that reference actual fields in the Lisp_String object, following conventions used elsewhere. rename set_string_{data,length} macros (the only ones to work with a Lisp_String_* instead of a Lisp_Object) to set_lispstringp_* to make the difference clear. try to be consistent about caps vs. lowercase in macro/inline-fun names for chars and such, which wasn't the case before. we now reserve caps either for XFOO_ macros that reference object fields (e.g. XSTRING_DATA) or for things that have non-function semantics, e.g. directly modifying an arg (BREAKUP_EMCHAR) or evaluating an arg (any arg) more than once. otherwise, use lowercase. here is a summary of most of the macros/inline funs changed by all of the above changes: BYTE_*_P -> byte_*_p XSTRING_BYTE -> string_byte set_string_data/length -> set_lispstringp_data/length XSTRING_CHAR_LENGTH -> string_char_length XSTRING_CHAR -> string_emchar INTBYTE_FIRST_BYTE_P -> intbyte_first_byte_p INTBYTE_LEADING_BYTE_P -> intbyte_leading_byte_p charptr_copy_char -> charptr_copy_emchar LEADING_BYTE_* -> leading_byte_* CHAR_* -> EMCHAR_* *_CHAR_* -> *_EMCHAR_* *_CHAR -> *_EMCHAR CHARSET_BY_ -> charset_by_* BYTE_SHIFT_JIS* -> byte_shift_jis* BYTE_BIG5* -> byte_big5* REP_BYTES_BY_FIRST_BYTE -> rep_bytes_by_first_byte char_to_unicode -> emchar_to_unicode valid_char_p -> valid_emchar_p Change intbyte_strcmp -> qxestrcmp_c (duplicated functionality). -- INTERFACE CHANGES AFFECTING LESS CODE: use DECLARE_INLINE_HEADER in various places. remove '#ifdef emacs' from XEmacs-only files. eliminate CHAR_TABLE_VALUE(), which duplicated the functionality of get_char_table(). add BUFFER_TEXT_LOOP to simplify iterations over buffer text. define typedefs for signed and unsigned types of fixed sizes (INT_32_BIT, UINT_32_BIT, etc.). create ALIGN_FOR_TYPE as a higher-level interface onto ALIGN_SIZE; fix code to use it. add charptr_emchar_len to return the text length of the character pointed to by a ptr; use it in place of charcount_to_bytecount(..., 1). add emchar_len to return the text length of a given character. add types Bytexpos and Charxpos to generalize Bytebpos/Bytecount and Charbpos/Charcount, in code (particularly, the extents code and redisplay code) that works with either kind of index. rename redisplay struct params with names such as `charbpos' to e.g. `charpos' when they are e.g. a Charxpos, not a Charbpos. eliminate xxDEFUN in place of DEFUN; no longer necessary with changes awhile back to doc.c. split up big ugly combined list of EXFUNs in lisp.h on a file-by-file basis, since other prototypes are similarly split. rewrite some "*_UNSAFE" macros as inline funs and eliminate the _UNSAFE suffix. move most string code from lisp.h to text.h; the string code and text.h code is now intertwined in such a fashion that they need to be in the same place and partially interleaved. (you can't create forward references for inline funs) automated/lisp-tests.el, automated/symbol-tests.el, automated/test-harness.el: Fix test harness to output FAIL messages to stderr when in batch mode. Fix up some problems in lisp-tests/symbol-tests that were causing spurious failures.
author ben
date Sun, 05 May 2002 11:33:57 +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);
	}
    }
}