OSDN Git Service

Implement GUI list view compiler for XML catalogue.
authorKeith Marshall <keithmarshall@users.sourceforge.net>
Mon, 8 Oct 2012 20:41:49 +0000 (21:41 +0100)
committerKeith Marshall <keithmarshall@users.sourceforge.net>
Mon, 8 Oct 2012 20:41:49 +0000 (21:41 +0100)
21 files changed:
ChangeLog
Makefile.in
icons/state01.ico [new file with mode: 0644]
icons/state02.ico [new file with mode: 0644]
icons/state03.ico [new file with mode: 0644]
icons/state04.ico [new file with mode: 0644]
icons/state05.ico [new file with mode: 0644]
icons/state06.ico [new file with mode: 0644]
icons/state07.ico [new file with mode: 0644]
icons/state08.ico [new file with mode: 0644]
icons/state09.ico [new file with mode: 0644]
icons/state10.ico [new file with mode: 0644]
icons/state11.ico [new file with mode: 0644]
icons/state12.ico [new file with mode: 0644]
icons/state13.ico [new file with mode: 0644]
src/guidata.rc
src/guimain.h
src/guixmld.cpp
src/pkglist.cpp [new file with mode: 0644]
src/pkglist.h
src/pkgview.cpp

index 6fd319a..add16c9 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,53 @@
+2012-10-08  Keith Marshall  <keithmarshall@users.sourceforge.net>
+
+       Implement GUI list view compiler for XML catalogue.
+
+       * src/pkgview.cpp (AppWindowMaker::LayoutEngine): Add case handler...
+       [pane_id == ID_PACKAGE_LISTVIEW]: ...for this window pane class.
+
+       * src/guimain.h (ID_PKGLIST_TABLE_HEADINGS):
+       (ID_PKGNAME_COLUMN_HEADING, ID_PKGTYPE_COLUMN_HEADING):
+       (ID_INSTVER_COLUMN_HEADING, ID_REPOVER_COLUMN_HEADING):
+       (ID_PKGDESC_COLUMN_HEADING): New resource identifiers; define them.
+       (ID_PKGSTATE_AVAILABLE, ID_PKGSTATE_AVAILABLE_NEW):
+       (ID_PKGSTATE_AVAILABLE_LOCKED, ID_PKGSTATE_AVAILABLE_INSTALL):
+       (ID_PKGSTATE_INSTALLED_CURRENT, ID_PKGSTATE_INSTALLED_LOCKED):
+       (ID_PKGSTATE_INSTALLED_OLD, ID_PKGSTATE_UPGRADE, ID_PKGSTATE_REMOVE):
+       (ID_PKGSTATE_REINSTALL, ID_PKGSTATE_DOWNGRADE, ID_PKGSTATE_BROKEN):
+       (ID_PKGSTATE_PURGE): New icon resource identifiers; define them.
+       (AppWindowMaker::InitPackageListView): New private method; declare it.
+       (AppWindowMaker::UpdatePackageList): New private method; declare it.
+       (AppWindowMaker::ClearPackageList): New private method; declare and
+       implement it inline.
+
+       * src/guidata.rc (ID_PKGSTATE_AVAILABLE, ID_PKGSTATE_AVAILABLE_NEW):
+       (ID_PKGSTATE_AVAILABLE_LOCKED, ID_PKGSTATE_AVAILABLE_INSTALL):
+       (ID_PKGSTATE_INSTALLED_CURRENT, ID_PKGSTATE_INSTALLED_LOCKED):
+       (ID_PKGSTATE_INSTALLED_OLD, ID_PKGSTATE_UPGRADE, ID_PKGSTATE_REMOVE):
+       (ID_PKGSTATE_REINSTALL, ID_PKGSTATE_DOWNGRADE, ID_PKGSTATE_BROKEN):
+       (ID_PKGSTATE_PURGE): New icon resources; implement them.
+
+       * icons/state01.ico icons/state02.ico icons/state03.ico:
+       * icons/state04.ico icons/state05.ico icons/state06.ico:
+       * icons/state07.ico icons/state08.ico icons/state09.ico:
+       * icons/state10.ico icons/state11.ico icons/state12.ico:
+       * icons/state13.ico: New files.  All are cloned from synaptic icons;
+       see src/guimain.rc for symbolic/functional reference names.
+
+       * src/pkglist.h [GUIMAIN_H defined] (pkgListViewMaker): New class;
+       declare it for use in modules which #include guimain.h
+
+       * src/pkglist.cpp: New file; it implements...
+       (pkgListViewMaker): ...this new class, and its clients...
+       (AppWindowMaker::InitPackageListView): New method.
+       (AppWindowMaker::UpdatePackageList): Likewise.
+
+       * Makefile.in (GUIMAIN_OBJECTS): Add pkglist.OBJEXT
+
+       * src/guixmld.cpp (AppWindowMaker::Invoke): Call InitCommonControls(),
+       as prerequisite to AppWindowMaker::InitPackageListView(); follow up by
+       calling AppWindowMaker::AdjustLayout().
+
 2012-10-07  Keith Marshall  <keithmarshall@users.sourceforge.net>
 
        Add GUI initialiser hook for XML catalogue interface.
index 797645b..146f475 100644 (file)
@@ -91,7 +91,7 @@ CLI_EXE_OBJECTS  =   \
 
 GUIMAIN_OBJECTS  =   \
    guimain.$(OBJEXT) guidata.$(OBJEXT) guixmld.$(OBJEXT) guidmh.$(OBJEXT) \
-   approot.$(OBJEXT) pkgview.$(OBJEXT)
+   approot.$(OBJEXT) pkgview.$(OBJEXT) pkglist.$(OBJEXT)
 
 GUIMAIN_LIBS = -lwtklite -lcomctl32
 
diff --git a/icons/state01.ico b/icons/state01.ico
new file mode 100644 (file)
index 0000000..1657a3a
Binary files /dev/null and b/icons/state01.ico differ
diff --git a/icons/state02.ico b/icons/state02.ico
new file mode 100644 (file)
index 0000000..d903273
Binary files /dev/null and b/icons/state02.ico differ
diff --git a/icons/state03.ico b/icons/state03.ico
new file mode 100644 (file)
index 0000000..e4066f2
Binary files /dev/null and b/icons/state03.ico differ
diff --git a/icons/state04.ico b/icons/state04.ico
new file mode 100644 (file)
index 0000000..93e4514
Binary files /dev/null and b/icons/state04.ico differ
diff --git a/icons/state05.ico b/icons/state05.ico
new file mode 100644 (file)
index 0000000..34e8203
Binary files /dev/null and b/icons/state05.ico differ
diff --git a/icons/state06.ico b/icons/state06.ico
new file mode 100644 (file)
index 0000000..6989422
Binary files /dev/null and b/icons/state06.ico differ
diff --git a/icons/state07.ico b/icons/state07.ico
new file mode 100644 (file)
index 0000000..9772e4f
Binary files /dev/null and b/icons/state07.ico differ
diff --git a/icons/state08.ico b/icons/state08.ico
new file mode 100644 (file)
index 0000000..861c02e
Binary files /dev/null and b/icons/state08.ico differ
diff --git a/icons/state09.ico b/icons/state09.ico
new file mode 100644 (file)
index 0000000..8d358ee
Binary files /dev/null and b/icons/state09.ico differ
diff --git a/icons/state10.ico b/icons/state10.ico
new file mode 100644 (file)
index 0000000..6f5d3d8
Binary files /dev/null and b/icons/state10.ico differ
diff --git a/icons/state11.ico b/icons/state11.ico
new file mode 100644 (file)
index 0000000..79f16ed
Binary files /dev/null and b/icons/state11.ico differ
diff --git a/icons/state12.ico b/icons/state12.ico
new file mode 100644 (file)
index 0000000..0036783
Binary files /dev/null and b/icons/state12.ico differ
diff --git a/icons/state13.ico b/icons/state13.ico
new file mode 100644 (file)
index 0000000..830ab4f
Binary files /dev/null and b/icons/state13.ico differ
index 7438cfc..d5ebf5a 100644 (file)
@@ -104,6 +104,29 @@ BEGIN
     MENUITEM   "Icon &Legend",                         IDM_HELP_LEGEND, GRAYED
     MENUITEM   "&About mingw-get",                     IDM_HELP_ABOUT
   END
+
 END
 
+/* The set of icons used in check-boxes representing package status;
+ * as in the case of the application icon, these are clones of their
+ * corresponding synaptic icons, with their file names representing
+ * the serial position of each within mingw-get's package list icon
+ * bundle, while their symbolic names are derived from the orignal
+ * synaptic icon names, which are representative of the associated
+ * package status.
+ */
+ID_PKGSTATE_AVAILABLE          ICON    DISCARDABLE     "state01.ico"
+ID_PKGSTATE_AVAILABLE_NEW      ICON    DISCARDABLE     "state02.ico"
+ID_PKGSTATE_AVAILABLE_LOCKED   ICON    DISCARDABLE     "state03.ico"
+ID_PKGSTATE_AVAILABLE_INSTALL  ICON    DISCARDABLE     "state04.ico"
+ID_PKGSTATE_INSTALLED_CURRENT  ICON    DISCARDABLE     "state05.ico"
+ID_PKGSTATE_INSTALLED_LOCKED   ICON    DISCARDABLE     "state06.ico"
+ID_PKGSTATE_INSTALLED_OLD      ICON    DISCARDABLE     "state07.ico"
+ID_PKGSTATE_UPGRADE            ICON    DISCARDABLE     "state08.ico"
+ID_PKGSTATE_REINSTALL          ICON    DISCARDABLE     "state09.ico"
+ID_PKGSTATE_DOWNGRADE          ICON    DISCARDABLE     "state10.ico"
+ID_PKGSTATE_BROKEN             ICON    DISCARDABLE     "state11.ico"
+ID_PKGSTATE_REMOVE             ICON    DISCARDABLE     "state12.ico"
+ID_PKGSTATE_PURGE              ICON    DISCARDABLE     "state13.ico"
+
 /* $RCSfile$: end of file */
index 978618c..ab9076b 100644 (file)
 #define IDM_PACKAGE_UPGRADE             403
 #define IDM_PACKAGE_REMOVE              404
 
+#define PKGSTATE(_STATE)       (ID_PKGSTATE_##_STATE - ID_PKGSTATE_AVAILABLE)
+#define ID_PKGSTATE(INDEX)     (INDEX + ID_PKGSTATE_AVAILABLE)
+
+#define ID_PKGSTATE_AVAILABLE           501
+#define ID_PKGSTATE_AVAILABLE_NEW       502
+#define ID_PKGSTATE_AVAILABLE_LOCKED    503
+#define ID_PKGSTATE_AVAILABLE_INSTALL   504
+#define ID_PKGSTATE_INSTALLED_CURRENT   505
+#define ID_PKGSTATE_INSTALLED_LOCKED    506
+#define ID_PKGSTATE_INSTALLED_OLD       507
+#define ID_PKGSTATE_UPGRADE             508
+#define ID_PKGSTATE_REINSTALL           509
+#define ID_PKGSTATE_DOWNGRADE           510
+#define ID_PKGSTATE_BROKEN              511
+#define ID_PKGSTATE_REMOVE              512
+#define ID_PKGSTATE_PURGE               513
+
 #define IDM_HELP_CONTENTS               600
 #define IDM_HELP_INTRO                  601
-#define IDM_HELP_LEGEND                         602
-#define IDM_HELP_ABOUT                          603
+#define IDM_HELP_LEGEND                 602
+#define IDM_HELP_ABOUT                  603
 #define IDD_HELP_ABOUT                  603
 
+#define ID_PKGLIST_TABLE_HEADINGS      1024
+#define ID_PKGNAME_COLUMN_HEADING      1025
+#define ID_PKGTYPE_COLUMN_HEADING      1026
+#define ID_INSTVER_COLUMN_HEADING      1027
+#define ID_REPOVER_COLUMN_HEADING      1028
+#define ID_PKGDESC_COLUMN_HEADING      1029
+
 #ifndef RC_INVOKED
 #define WIN32_LEAN_AND_MEAN
 /*
@@ -69,6 +93,7 @@
  *
  */
 #include <wtklite.h>
+#include <commctrl.h>
 
 class pkgXmlDocument;
 
@@ -107,6 +132,11 @@ class AppWindowMaker: public WTK::MainWindowMaker
     pkgXmlDocument *pkgData;
     void LoadPackageData( bool = false );
     HFONT DefaultFont;
+
+    HWND PackageListView;
+    void InitPackageListView( void );
+    void ClearPackageList( void ){ ListView_DeleteAllItems( PackageListView ); }
+    void UpdatePackageList( void );
 };
 
 #endif /* ! RC_INVOKED */
index 54916b1..1fa4a81 100644 (file)
@@ -38,8 +38,26 @@ int AppWindowMaker::Invoked( void )
    * and creation of the display controls through which its content
    * will be presented to the user, prior to invocation of the main
    * window's message loop.
+   *
+   * The data displays depend on the MS-Windows Common Controls API;
+   * initialise all components of this up front.
+   */
+  InitCommonControls();
+
+  /* Load the data from the XML catalogue files, and construct the
+   * initial view of the available package list.
    */
   LoadPackageData();
+  InitPackageListView();
+
+  /* Force a layout adjustment, to ensure that the displayed
+   * data controls are correctly populated.
+   */
+  AdjustLayout();
+
+  /* Finally, we may delegate all further processing to the main
+   * window's message loop.
+   */
   return WTK::MainWindowMaker::Invoked();
 }
 
diff --git a/src/pkglist.cpp b/src/pkglist.cpp
new file mode 100644 (file)
index 0000000..c22d6f8
--- /dev/null
@@ -0,0 +1,419 @@
+/*
+ * pkglist.cpp
+ *
+ * $Id$
+ *
+ * Written by Keith Marshall <keithmarshall@users.sourceforge.net>
+ * Copyright (C) 2012, MinGW Project
+ *
+ *
+ * Implementation of the methods for the pkgListViewMaker class, to
+ * support the display of the package list in the mingw-get graphical
+ * user interface.
+ *
+ *
+ * This is free software.  Permission is granted to copy, modify and
+ * redistribute this software, under the provisions of the GNU General
+ * Public License, Version 3, (or, at your option, any later version),
+ * as published by the Free Software Foundation; see the file COPYING
+ * for licensing details.
+ *
+ * Note, in particular, that this software is provided "as is", in the
+ * hope that it may prove useful, but WITHOUT WARRANTY OF ANY KIND; not
+ * even an implied WARRANTY OF MERCHANTABILITY, nor of FITNESS FOR ANY
+ * PARTICULAR PURPOSE.  Under no circumstances will the author, or the
+ * MinGW Project, accept liability for any damages, however caused,
+ * arising from the use of this software.
+ *
+ */
+#define _WIN32_IE  0x0300
+
+#include "guimain.h"
+#include "pkgbase.h"
+#include "pkglist.h"
+#include "pkgkeys.h"
+#include "pkginfo.h"
+
+#include <wtkexcept.h>
+
+void AppWindowMaker::InitPackageListView()
+{
+  /* Create and initialise a ListView window, in which to present
+   * the package list...
+   */
+  PackageListView = CreateWindow( WC_LISTVIEW, NULL,
+      WS_VISIBLE | WS_BORDER | WS_CHILD | WS_EX_CLIENTEDGE |
+      LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS, 0, 0, 0, 0,
+      AppWindow, (HMENU)(ID_PACKAGE_LISTVIEW),
+      AppInstance, NULL
+    );
+  /* ...and set its extended style attributes.
+   */
+  ListView_SetExtendedListViewStyle( PackageListView,
+      LVS_EX_GRIDLINES | LVS_EX_FULLROWSELECT | LVS_EX_ONECLICKACTIVATE
+    );
+
+  /* Propagate the default font, as set for the main window itself,
+   * to the list view control.
+   */
+  SendMessage( PackageListView, WM_SETFONT, (WPARAM)(DefaultFont), TRUE );
+
+  /* Assign an image list, to furnish the package status icons.
+   */
+  HIMAGELIST iconlist = ImageList_Create( 16, 16, ILC_COLOR32 | ILC_MASK, 13, 0 );
+  for( int index = 0; index <= PKGSTATE( PURGE ); index++ )
+  {
+    HICON image = LoadIcon( AppInstance, MAKEINTRESOURCE(ID_PKGSTATE(index)) );
+    if( ImageList_AddIcon( iconlist, image ) == -1 )
+      throw WTK::runtime_error( "Error loading package status icons" );
+  }
+  ListView_SetImageList( PackageListView, iconlist, LVSIL_SMALL );
+
+  /* Initialise the table layout, and assign column headings.
+   */
+  LVCOLUMN table;
+  struct { int id; int width; char *text; } headings[] =
+  {
+    /* Specify the column headings for the package list table.
+     */
+    { ID_PKGLIST_TABLE_HEADINGS,  20, ""  },
+    { ID_PKGNAME_COLUMN_HEADING, 150, "Package" },
+    { ID_PKGTYPE_COLUMN_HEADING,  48, "Class" },
+    { ID_INSTVER_COLUMN_HEADING, 125, "Installed Version" },
+    { ID_REPOVER_COLUMN_HEADING, 125, "Repository Version" },
+    { ID_PKGDESC_COLUMN_HEADING, 400, "Description" },
+    /*
+     * This all-null entry terminates the sequence.
+     */
+    { 0, 0, NULL }
+  };
+  table.fmt = LVCFMT_LEFT;
+  table.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
+  for( int index = 0; headings[index].text != NULL; index++ )
+  {
+    /* Loop over the columns, setting the initial width
+     * (in pixels), and assigning the heading to each.
+     */
+    table.cx = headings[index].width;
+    table.pszText = headings[index].text;
+    ListView_InsertColumn( PackageListView, index, &table );
+  }
+  /* "Update" the package list, to initialise the list view.
+   */
+  UpdatePackageList();
+}
+
+void AppWindowMaker::UpdatePackageList()
+{
+  /* Scan the XML package catalogue, adding a ListView item entry
+   * for each package record.
+   */
+  pkgListViewMaker PackageList( PackageListView );
+  pkgDirectory *dir = pkgData->CatalogueAllPackages();
+  dir->InOrder( &PackageList );
+  delete dir;
+}
+
+pkgListViewMaker::pkgListViewMaker( HWND pane ): ListView( pane )
+{
+  /* Constructor: initialise the invariant parameters within the
+   * embedded W32API ListView control structure.
+   */
+  content.stateMask = 0;
+  content.mask = LVIF_PARAM | LVIF_TEXT | LVIF_IMAGE | LVIF_STATE;
+  content.iItem = -1;
+}
+
+void pkgListViewMaker::InsertItem( pkgXmlNode *pkg, char *class_name )
+{
+  /* Private method to add a single package record, as an individual
+   * row item, to the displayed list view table.
+   */
+  content.state = 0;
+  content.iItem += 1;
+  content.iSubItem = 0;
+  content.lParam = (unsigned long)(pkg);
+  content.pszText = "";
+
+  /* Assign a temporary action item, through which we may identify
+   * the latest available, and currently installed (if any), version
+   * attributes for the package under consideration.
+   */
+  pkgActionItem avail;
+  pkgXmlNode *rel = pkg->FindFirstAssociate( release_key );
+  while( rel != NULL )
+  {
+    /* Examine each available release specification for the nominated
+     * package; select the one which represents the latest (most recent)
+     * available release.
+     */
+    avail.SelectIfMostRecentFit( rel );
+
+    /* Also check for the presence of an installation record for each
+     * release; if found, mark it as the currently installed release;
+     * (we assign the "to-remove" attribute, but we don't action it).
+     */
+    if( rel->GetInstallationRecord( rel->GetPropVal( tarname_key, NULL )) != NULL )
+      avail.SelectPackage( rel, to_remove );
+
+    /* Cycle, until all known releases have been examined.
+     */
+    rel = rel->FindNextAssociate( release_key );
+  }
+  /* Check the identification of any currently installed release; this
+   * will capture property data for any installed release which may have
+   * been subsequently withdrawn from distribution.
+   */
+  avail.ConfirmInstallationStatus();
+
+  /* Decompose the package tarname identifier for the latest available
+   * release, into its individual package specification properties.
+   */
+  pkgSpecs latest( rel = avail.Selection() );
+
+  /* Allocate a temporary working text buffer, in which to format
+   * package property values for display...
+   */
+  size_t len = strlen( rel->GetPropVal( tarname_key, value_none ) );
+  if( (rel = avail.Selection( to_remove )) != NULL )
+  {
+    /* ...ensuring that it is at least as large as the longer of the
+     * latest or installed release tarname.
+     */
+    size_t altlen = strlen( rel->GetPropVal( tarname_key, value_none ) );
+    if( altlen > len ) len = altlen;
+  }
+  char buf[1 + len];
+
+  /* Choose a suitable icon for representation of the installation
+   * status of the package under consideration...
+   */
+  if( rel != NULL )
+  {
+    /* ...noting that, when it is already installed...
+     */
+    pkgSpecs current( rel );
+    if( latest > current )
+      /*
+       * ...and, when the latest available is NEWER than the
+       * installed version, then we choose the icon indicating
+       * an installed package with an available update...
+       */
+      content.iImage = PKGSTATE( INSTALLED_OLD );
+    else
+      /* ...or, when the latest available is NOT NEWER than
+       * the installed version, then we choose the alternative
+       * icon, simply indicating an installed package...
+       */
+      content.iImage = PKGSTATE( INSTALLED_CURRENT );
+
+    /* ...and also, load the version identification string for
+     * the installed version into the working text buffer.
+     */
+    GetVersionString( buf, &current );
+  }
+  else
+    /* Alternatively, for any package which is not recorded as
+     * installed, choose the icon indicating an available, but
+     * not (yet) installed package.
+     */
+    content.iImage = PKGSTATE( AVAILABLE );
+
+  /* Add the package identification record, as a list item...
+   */
+  ListView_InsertItem( ListView, &content );
+  /*
+   * ...and fill in the text for the package name and class columns.
+   */
+  ListView_SetItemText( ListView, content.iItem, 1, package_name );
+  ListView_SetItemText( ListView, content.iItem, 2, class_name );
+
+  /* When an installed package release has been identified...
+   */
+  if( rel != NULL )
+    /*
+     * ...fill in the version identificaton text appropriately.
+     */
+    ListView_SetItemText( ListView, content.iItem, 3, buf );
+
+  /* Finally, fill in the text for the latest version identification
+   * and package description columns.
+   */
+  ListView_SetItemText( ListView, content.iItem, 4, GetVersionString( buf, &latest ) );
+  ListView_SetItemText( ListView, content.iItem, 5, GetTitle( avail.Selection()) );
+}
+
+char *pkgListViewMaker::GetTitle( pkgXmlNode *pkg, const pkgXmlNode *xml_root )
+{
+  /* A private helper method, to retrieve the title attribute
+   * associated with the description for the nominated package.
+   *
+   * Note: this should really return a const char *, but then
+   * we would need to cast it to non-const for mapping into the
+   * ill-formed structure of Microsoft's LVITEM, so we may just
+   * as well return the non-const result anyway.
+   */
+  pkgXmlNode *desc = pkg->FindFirstAssociate( description_key );
+  while( desc != NULL )
+  {
+    /* Handling it internally as the const which it should be...
+     */
+    const char *title;
+    if( (title = desc->GetPropVal( title_key, NULL )) != NULL )
+    {
+      /* As soon as we find a description element with an
+       * assigned title attribute...
+       */
+#if 0
+      /* ...and noting that some package descriptions may
+       * redundantly respecify the package name as a colon
+       * delimited prefix to this title attribute...
+       *
+       * FIXME: ultimately, I'd like to remove this hack;
+       * package descriptions have no need to redundantly
+       * specify the package name as a prefix to the title,
+       * (since mingw-get can retrieve it from the package
+       * element containing the description anyway), and
+       * package maintainers should not rely on this
+       * hack to clean up the redundancy.
+       */
+      for( const char *prefix = title; *prefix != '\0'; prefix++ )
+       if( *prefix == ':' )
+       {
+         /* ...we strip that redundant prefix away...
+          */
+         title = ++prefix;
+         prefix = "";
+       }
+      /* ...then trim away any leading white space...
+       */
+      while( isspace( *title ) )
+       ++title;
+#endif
+      /* ...before immediately returning the title, (with the
+       * required cast to non-const).
+       */
+      return (char *)(title);
+    }
+
+    /* If we haven't yet found any title attribute, check for any
+     * further description elements at the current XML nesting level.
+     */
+    desc = desc->FindNextAssociate( description_key );
+  }
+
+  /* If we've exhausted all description elements at the current XML
+   * nesting level, without finding a title attribute, and we haven't
+   * checked all enclosing levels back to the document root...
+   */
+  if( pkg != xml_root )
+    /*
+     * ...then continue the search in the immediately enclosing level.
+     */
+    return GetTitle( pkg->GetParent() );
+
+  /* If we get to here, then we've searched all levels back to the
+   * document root, and have failed to find any title attribute; we
+   * have nothing to return.
+   */
+  return NULL;
+}
+
+static inline
+char *version_string_copy( char *buf, const char *text, int fill = 0 )
+{
+  /* Local helper function to construct a package version string
+   * from individual version specific elements of the tarname.
+   */
+  if( text != NULL )
+  {
+    /* First, if a fill character is specified, copy it as the
+     * first character of the result; (we assume that we are
+     * appending to a previously constructed result, and that
+     * this is the field separator character).
+     */
+    if( fill != 0 ) *buf++ = fill;
+
+    /* Now, append "text" up to, and including its final NUL
+     * terminator; (note that we do NOT guard against buffer
+     * overrun, as we have complete control over the calling
+     * context, where we allocated a result buffer at least
+     * as long as the tarname string from which the composed
+     * version string is extracted, and the composed result
+     * can never exceed the original length of this).
+     */
+    do { if( (*buf = *text) != '\0' ) ++buf; } while( *text++ != '\0' );
+  }
+  /* Finally, we return a pointer to the terminating NUL of
+   * the result, so as to facilitate appending further text.
+   */
+  return buf;
+}
+
+char *pkgListViewMaker::GetVersionString( char *buf, pkgSpecs *pkg )
+{
+  /* Helper method to construct a fully qualified version string
+   * from the decomposed package tarname form in a pkgSpecs structure.
+   *
+   * We begin with the concatenation of package version and build ID
+   * fields, retrieved from the pkgSpecs representation...
+   */
+  char *update = version_string_copy( buf, pkg->GetPackageVersion() );
+  update = version_string_copy( update, pkg->GetPackageBuild(), '-' );
+  if( pkg->GetSubSystemVersion() != NULL )
+  {
+    /* ...then, we append the sub-system ID, if applicable...
+     */
+    update = version_string_copy( update, pkg->GetSubSystemName(), '-' );
+    update = version_string_copy( update, pkg->GetSubSystemVersion(), '-' );
+    update = version_string_copy( update, pkg->GetSubSystemBuild(), '-' );
+  }
+  /* ...and finally, we return a pointer to the buffer in which
+   * we constructed the fully qualified version string.
+   */
+  return buf;
+}
+
+void pkgListViewMaker::Dispatch( pkgXmlNode *package )
+{
+  /* Implementation of the "dispatcher" method, which is required
+   * by any derivative of the pkgDirectoryViewerEngine class, for
+   * dispatching the content of the directory to the display service,
+   * (which, in this case, populates the list view window pane).
+   */
+  if( package->IsElementOfType( package_key ) )
+  {
+    /* Assemble the package name into the list view record block.
+     */
+    package_name = (char *)(package->GetPropVal( name_key, value_unknown ));
+
+    /* When processing a pkgDirectory entry for a package entity,
+     * generate a sub-directory listing for any contained package
+     * components...
+     */
+    pkgDirectory *dir;
+    if( (dir = EnumerateComponents( package )) != NULL )
+    {
+      /* ...and recurse, to process any which are found...
+       */
+      dir->InOrder( this );
+      delete dir;
+    }
+    else
+      /* ...otherwise, simply insert an unclassified list entry
+       * for the bare package name, omitting the component class.
+       */
+      InsertItem( package, "" );
+  }
+  else if( package->IsElementOfType( component_key ) )
+  {
+    /* Handle the recursive calls for the component sub-directory,
+     * inheriting the package name entry from the original package
+     * entity, and emit an appropriately classified list view entry
+     * for each identified component package.
+     */
+    InsertItem( package, (char *)(package->GetPropVal( class_key, "" )) );
+  }
+}
+
+/* $RCSfile$: end of file */
index cbc04c0..da63412 100644 (file)
@@ -46,6 +46,41 @@ class pkgDirectoryViewerEngine
     pkgDirectory *EnumerateComponents( pkgXmlNode * );
 };
 
+#ifdef GUIMAIN_H
+/*
+ * The following class is required only when implementing the
+ * graphical user interface; it is heavily dependent on graphical
+ * elements of the MS-Windows API.  Thus, we expose its declaration
+ * only when the including module expresses an intent to deploy
+ * such graphical elements, (as implied by prior inclusion of
+ * the guimain.h header, which this augments).
+ *
+ */
+class pkgListViewMaker: public pkgDirectoryViewerEngine
+{
+  /* A concrete specialization of the pkgDirectoryViewerEngine
+   * class, used to assemble the content of the records displayed
+   * in the list view pane of the graphical user interface.
+   */
+  public:
+    pkgListViewMaker( HWND );
+    virtual void Dispatch( pkgXmlNode * );
+
+  private:
+    HWND ListView;
+    LVITEM content;
+    char *GetTitle( pkgXmlNode *pkg )
+    {
+      return GetTitle( pkg, pkg->GetDocumentRoot() );
+    }
+    char *GetTitle( pkgXmlNode *, const pkgXmlNode * );
+    char *GetVersionString( char *, pkgSpecs * );
+    void InsertItem( pkgXmlNode *, char * );
+    char *package_name;
+};
+
+#endif /* GUIMAIN_H */
+
 class pkgDirectory
 {
   /* A locally defined class, used to manage a list of package
index a04bbb4..711e259 100644 (file)
@@ -203,6 +203,19 @@ int AppWindowMaker::LayoutEngine( HWND pane, LPARAM region )
     /* Each of the panes and sashes is computed individually, in the
      * following order:
      */
+    case ID_PACKAGE_LISTVIEW:
+      /* Upper right hand pane; occupies the full width of the parent
+       * window which remains to the right of the tree view, (after an
+       * allowance for a small gap to accommodate the sash bar has been
+       * deducted); its upper edge is co-incident with the top of the
+       * parent, and its height is set to the fraction of the parent's
+       * height specified as...
+       */
+      pane_height = VerticalSash->Displacement( pane_height );
+      pane_left = HorizontalSash->Displacement( pane_width ) + SASH_BAR_THICKNESS;
+      pane_width -= pane_left;
+      break;
+
     case ID_HORIZONTAL_SASH:
       /* A slim window, placed to the right of the tree view pane, and
        * occupying the full height of the parent window, representing the