OSDN Git Service

Initiate progress metering for catalogue load and update operations.
authorKeith Marshall <keithmarshall@users.sourceforge.net>
Sat, 27 Oct 2012 08:12:15 +0000 (09:12 +0100)
committerKeith Marshall <keithmarshall@users.sourceforge.net>
Sat, 27 Oct 2012 08:12:15 +0000 (09:12 +0100)
ChangeLog
src/guidata.rc
src/guimain.h
src/guixmld.cpp
src/pkgbase.h
src/pkgbind.cpp
src/pkgview.cpp

index 0d76564..f30ae95 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,53 @@
+2012-10-27  Keith Marshall  <keithmarshall@users.sourceforge.net>
+
+       Initiate progress metering for catalogue load and update operations.
+
+       * src/guidata.rc (IDD_REPO_UPDATE): New dialogue template; define it.
+       (IDM_REPO_UPDATE): Enable selection from "Repository" drop-down menu.
+
+       * src/guimain.h (IDD_REPO_UPDATE, IDD_CLOSE_OPTIONS):
+       (IDD_AUTO_CLOSE_OPTION, IDD_PROGRESS_BAR, IDD_PROGRESS_MSG): New
+       resource identification manifest constants; define them.
+       (pkgXmlNode, pkgProgressMeter): Add forward class declarations.
+       (AppWindowMaker::AttachedProgressMeter): New private data; define...
+       (AppWindowMaker::AppWindowMaker): ...and initialise it.
+       (AppWindowMaker::AttachProgressMeter): New public method; declare it.
+       (AppWindowMaker::DetachProgressMeter): Likewise.
+       (AppWindowMaker::LoadPackageData, AppWindowMaker::ClearPackageList):
+       (AppWindowMaker::UpdatePackageList): Make them public.
+
+       * src/pkgbase.h (pkgProgressMeter): New abstract class; declare it.
+       (AppWindowMaker) [GUIMAIN_H undefined]: Add forward class declaration.
+       (pkgXmlDocument::progress_meter): New private data member; declare...
+       (pkgXmlDocument::pkgXmlDocument): ...and initialise it.
+       (pkgXmlDocument::ProgressMeter, pkgXmlDocument::AttachProgressMeter):
+       (pkgXmlDocument::DetachProgressMeter): New public inline methods;
+       implement them.
+
+       * src/pkgview.cpp (WTK::GenericDialogue): Delete disused reference.
+       (AppWindowMaker::OnCommand): Factor out implementation; relocate it...
+       * src/guixmld.cpp: ...to here; thus it may utilise dependants of...
+       (ProgressMeterMaker): ...this new locally implemented class.
+       (AppWindowMaker::AttachProgressMeter): Implement it.
+       (AppWindowMaker::AttachProgressMeter): Likewise.
+       (pkgProgressMeter::~pkgProgressMeter): Likewise.
+       (pkgUpdate, pkgInvokeUpdate): New static functions; implement them.
+       (AppWindowMaker::Invoked): Replace direct call to LoadPackageData by
+       an indirect invocation, via a progress metering dialogue box using...
+       (pkgInitDataLoad): ...this new static callback function, invoking...
+       (pkgInvokeInitDataLoad): ...this new static thread function, whence...
+       (AppWindowMaker::LoadPackageData): ...this; add hook-up call to...
+       (pkgXmlDocument::AttachProgressMeter): ...incorporate this.
+
+       * src/pkgbind.cpp (pkgRepository::total, pkgRepository::count): New
+       private static properties; declare and instantiate them; provide...
+       (pkgRepository::Reset, pkgRepository::IncrementTotal): ...new public
+       static methods to manipulate them; declare and implement them inline.
+       (pkgRepository::GetPackageList): Use them to manage updates to the
+       pkgProgressMeter class instance, if any, which has been bound to
+       the controlling pkgXmlDocument class instance, when invoking...
+       (pkgXmlXmlDocument::BindRepositories): ...this.
+
 2012-10-18  Keith Marshall  <keithmarshall@users.sourceforge.net>
 
        Associate DMH message boxes with active dialogues.
index 49b5f5b..5c7888a 100644 (file)
@@ -73,7 +73,7 @@ BEGIN
    */
   POPUP        "&Repository"
   BEGIN
-    MENUITEM     "&Update Catalogue",                  IDM_REPO_UPDATE, GRAYED
+    MENUITEM     "&Update Catalogue",                  IDM_REPO_UPDATE
     MENUITEM     SEPARATOR
     MENUITEM     "&Quit\tAlt+F4",                      IDM_REPO_QUIT
   END
@@ -130,4 +130,20 @@ ID_PKGSTATE_BROKEN         ICON    DISCARDABLE     "state11.ico"
 ID_PKGSTATE_REMOVE             ICON    DISCARDABLE     "state12.ico"
 ID_PKGSTATE_PURGE              ICON    DISCARDABLE     "state13.ico"
 
+/* Template for progress meter dialogue box.
+ */
+IDD_REPO_UPDATE DIALOG DISCARDABLE     10, 20, 270, 60
+CAPTION                                "Update Package Catalogue"
+STYLE                                  DS_MODALFRAME | DS_SETFONT | WS_POPUP | WS_CAPTION | WS_DLGFRAME
+FONT                                   10, "Verdana"
+BEGIN
+  GROUPBOX                             "Actions", IDD_CLOSE_OPTIONS, 5, 31, 260, 25
+  DEFPUSHBUTTON                        "Close", IDOK, 219, 39, 40, 12, WS_GROUP | WS_DISABLED
+  AUTOCHECKBOX                         "Close dialogue automatically, when update is complete.", \
+                                       IDD_AUTO_CLOSE_OPTION, 10, 41, 200, 11
+  CONTROL                              "", IDD_PROGRESS_BAR, PROGRESS_CLASS, WS_CHILD \
+                                       | PBS_SMOOTH, 6, 20, 258, 10
+  LTEXT                                "", IDD_PROGRESS_MSG, 7, 6, 256, 12
+END
+
 /* $RCSfile$: end of file */
index 275e7c3..57f4c6f 100644 (file)
 #define IDM_HELP_ABOUT                  603
 #define IDD_HELP_ABOUT                  603
 
+#define IDD_REPO_UPDATE                 610
+#define IDD_CLOSE_OPTIONS               611
+#define IDD_AUTO_CLOSE_OPTION           612
+#define IDD_PROGRESS_BAR                613
+#define IDD_PROGRESS_MSG                614
+
 #define ID_PKGLIST_TABLE_HEADINGS      1024
 #define ID_PKGNAME_COLUMN_HEADING      1025
 #define ID_PKGTYPE_COLUMN_HEADING      1026
 #include <wtklite.h>
 #include <commctrl.h>
 
+class pkgXmlNode;
 class pkgXmlDocument;
+class pkgProgressMeter;
 class DataSheetMaker;
 
 class AppWindowMaker;
@@ -116,13 +124,21 @@ class AppWindowMaker: public WTK::MainWindowMaker
 {
   public:
     AppWindowMaker( HINSTANCE inst ): WTK::MainWindowMaker( inst ),
-    pkgData( NULL ), DefaultFont( (HFONT)(GetStockObject( DEFAULT_GUI_FONT )) ){}
+    pkgData( NULL ), DefaultFont( (HFONT)(GetStockObject( DEFAULT_GUI_FONT )) ),
+    AttachedProgressMeter( NULL ){}
     ~AppWindowMaker(){ /* delete ChildWindows; */ DeleteObject( DefaultFont ); }
 
     HWND Create( const char *, const char * );
     inline long AdjustLayout( void ){ return OnSize( 0, 0, 0 ); }
     int Invoked( void );
 
+    void LoadPackageData( bool = false );
+    void ClearPackageList( void ){ ListView_DeleteAllItems( PackageListView ); }
+    void UpdatePackageList( void );
+
+    inline pkgProgressMeter *AttachProgressMeter( pkgProgressMeter * );
+    inline void DetachProgressMeter( pkgProgressMeter * );
+
   private:
     virtual long OnCreate();
     virtual long OnCommand( WPARAM );
@@ -134,13 +150,11 @@ class AppWindowMaker: public WTK::MainWindowMaker
     WTK::SashWindowMaker *HorizontalSash, *VerticalSash;
 
     pkgXmlDocument *pkgData;
-    void LoadPackageData( bool = false );
+    pkgProgressMeter *AttachedProgressMeter;
     HFONT DefaultFont;
 
     HWND PackageListView;
     void InitPackageListView( void );
-    void ClearPackageList( void ){ ListView_DeleteAllItems( PackageListView ); }
-    void UpdatePackageList( void );
 
     DataSheetMaker *DataSheet;
     WTK::ChildWindowMaker *TabDataPane;
index 08bcf0b..cf9112c 100644 (file)
 #include <unistd.h>
 #include <wtkexcept.h>
 
-int AppWindowMaker::Invoked( void )
+class ProgressMeterMaker: public pkgProgressMeter
 {
-  /* Override for the WTK::MainWindowMaker::Invoked() method; it
-   * provides the hook for the initial loading of the XML database,
-   * 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.
+  /* A locally defined class, supporting progress metering
+   * for package catalogue update and load operations.
    */
-  InitCommonControls();
+  public:
+    ProgressMeterMaker( HWND, HWND, AppWindowMaker * );
+
+    virtual int Annotate( const char *, ... );
+    virtual void SetRange( int, int );
+    virtual void SetValue( int );
+
+  protected:
+    HWND message_line, progress_bar;
+};
 
-  /* Load the data from the XML catalogue files, and construct the
-   * initial view of the available package list.
+inline
+pkgProgressMeter *AppWindowMaker::AttachProgressMeter( pkgProgressMeter *meter )
+{
+  /* A local helper method for attaching a progress meter to the
+   * controlling class instance for the main application window.
    */
-  LoadPackageData();
-  InitPackageListView();
+  if( AttachedProgressMeter == NULL )
+    AttachedProgressMeter = meter;
+  return AttachedProgressMeter;
+}
 
-  /* Initialise the data-sheet tab control, displaying the default
-   * "no package selected" message.
+inline void AppWindowMaker::DetachProgressMeter( pkgProgressMeter *meter )
+{
+  /* A local helper method for detaching a progress meter from the
+   * controlling class instance for the main application window.
    */
-  InitPackageTabControl();
+  if( meter == AttachedProgressMeter )
+  {
+    pkgData->DetachProgressMeter( meter );
+    AttachedProgressMeter = NULL;
+  }
+}
 
-  /* Force a layout adjustment, to ensure that the displayed
-   * data controls are correctly populated.
+/* We need to provide a destructor for the abstract base class, from which
+ * our progress meters are derived; here is as good a place as any.
+ */
+pkgProgressMeter::~pkgProgressMeter(){ referrer->DetachProgressMeter( this ); }
+
+/* We must also provide the implementation of our local progress meter class.
+ */
+ProgressMeterMaker::ProgressMeterMaker
+( HWND annotation, HWND indicator, AppWindowMaker *owner ):
+pkgProgressMeter( owner ), message_line( annotation ), progress_bar( indicator )
+{
+  /* Constructor creates an instance of the progress meter class, attaching it
+   * to the main application window, with an initial metering range of 0..100%,
+   * and a starting indicated completion state of 0%.
    */
-  AdjustLayout();
+  owner->AttachProgressMeter( this );
+  SetRange( 0, 100 );
+  SetValue( 0 );
+}
 
-  /* Finally, we may delegate all further processing to the main
-   * window's message loop.
+int ProgressMeterMaker::Annotate( const char *fmt, ... )
+{
+  /* Method to add a printf() style annotation to the progress meter dialogue.
    */
-  return WTK::MainWindowMaker::Invoked();
+  va_list argv;
+  va_start( argv, fmt );
+  char annotation[1 + vsnprintf( NULL, 0, fmt, argv )];
+  int len = vsnprintf( annotation, sizeof( annotation ), fmt, argv );
+  va_end( argv );
+
+  SendMessage( message_line, WM_SETTEXT, 0, (LPARAM)(annotation) );
+  return len;
 }
 
+void ProgressMeterMaker::SetRange( int min, int max )
+{
+  /* Method to adjust the range of the progress meter, to represent any
+   * arbitrary range of discrete values, rather than percentage units.
+   */
+  SendMessage( progress_bar, PBM_SETRANGE, 0, MAKELPARAM( min, max ) );
+}
+
+void ProgressMeterMaker::SetValue( int value )
+{
+  /* Method to update the indicated completion state of a progress meter,
+   * to represent any arbitrary value within its assigned metering range.
+   */
+  SendMessage( progress_bar, PBM_SETPOS, value, 0 );
+}
+
+/* Implementation of service routines, for loading the package catalogue
+ * from its defining collection of XML files.
+ */
 void AppWindowMaker::LoadPackageData( bool force_update )
 {
   /* Helper method to load the package database from its
@@ -119,6 +176,7 @@ void AppWindowMaker::LoadPackageData( bool force_update )
   /* Establish the repository URI references, for retrieval
    * of the downloadable catalogue files, and load them...
    */
+  pkgData->AttachProgressMeter( AttachedProgressMeter );
   if( pkgData->BindRepositories( force_update ) == NULL )
     /*
      * ...once again, bailing out on failure.
@@ -131,4 +189,251 @@ void AppWindowMaker::LoadPackageData( bool force_update )
   pkgData->LoadSystemMap();
 }
 
+static void pkgInvokeInitDataLoad( void *window )
+{
+  /* Thread procedure for performing the initial catalogue load, on
+   * application start-up.  This will load from locally cached data
+   * files, when available; however, it will also initiate a download
+   * from the remote repository, for any file which is missing from
+   * the local cache.  Since this may be a time consuming process,
+   * we subject it to progress metering, to ensure that the user is
+   * not left staring at an apparently hung, blank window.
+   */
+  HWND msg = GetDlgItem( (HWND)(window), IDD_PROGRESS_MSG );
+  HWND dlg = GetDlgItem( (HWND)(window), IDD_PROGRESS_BAR );
+  AppWindowMaker *app = GetAppWindow( GetParent( (HWND)(window) ));
+  SendMessage( (HWND)(window),
+      WM_SETTEXT, 0, (LPARAM)("Loading Package Catalogue")
+    );
+  ProgressMeterMaker ui( msg, dlg, app );
+
+  /* For this activity, we request automatic dismissal of the dialogue,
+   * when loading has been completed; the user will have an opportunity
+   * to countermand this choice, if loading is delayed by the required
+   * download of any missing local catalogue file.
+   */
+  dlg = GetDlgItem( (HWND)(window), IDD_AUTO_CLOSE_OPTION );
+  SendMessage( dlg, WM_SETTEXT, 0,
+      (LPARAM)("Close dialogue automatically, when loading is complete.")
+    );
+  CheckDlgButton( (HWND)(window), IDD_AUTO_CLOSE_OPTION, BST_CHECKED );
+
+  /* We've now set up the initial state for the progress meter dialogue;
+   * proceed to load, (and perhaps download), the XML data files.
+   */
+  app->LoadPackageData( false );
+
+  /* When loading has been completed, automatically dismiss the dialogue...
+   */
+  if( IsDlgButtonChecked( (HWND)(window), IDD_AUTO_CLOSE_OPTION ) )
+    SendMessage( (HWND)(window), WM_COMMAND, (WPARAM)(IDOK), 0 );
+
+  /* ...unless the user has countermanded the automatic dismissal request...
+   */
+  else
+  { /* ...in which case, we activate the manual dismissal button...
+     */
+    if( (dlg = GetDlgItem( (HWND)(window), IDOK )) != NULL )
+      EnableWindow( dlg, TRUE );
+
+    /* ...and notify the user that it must be clicked to continue.
+     */
+    ui.Annotate( "Data has been loaded; please close this dialogue to continue." );
+  }
+}
+
+static int CALLBACK pkgInitDataLoad
+( HWND window, unsigned int msg, WPARAM wParam, LPARAM lParam )
+{
+  /* Handler for the initial catalogue loading progress dialogue.
+   */
+  switch( msg )
+  {
+    /* We need to handle only two classes of windows messages
+     * on behalf of this dialogue box...
+     */
+    case WM_INITDIALOG:
+      /*
+       * ...viz. on initial dialogue box creation, we delegate the actual
+       * activity, of loading the catalogue, to this background thread...
+       */
+      _beginthread( pkgInvokeInitDataLoad, 0, (void *)(window) );
+      return TRUE;
+
+    case WM_COMMAND:
+      if( LOWORD( wParam ) == IDOK )
+      {
+       /* ...then we wait for a notification that the dialogue may be
+        * closed, (which isn't permitted until the thread completes).
+        */
+       EndDialog( window, 0 );
+       return TRUE;
+      }
+  }
+  /* Any other messages, which are directed to this dialogue box, may be
+   * safely ignored.
+   */
+  return FALSE;
+}
+
+static void pkgInvokeUpdate( void *window )
+{
+  /* Thread procedure for performing a package catalogue update.
+   * This will download catalogue files from the remote repository,
+   * and integrate them into the locally cached catalogue XML file
+   * set.  Since this is normally a time consuming process, we must
+   * subject it to progress metering, to ensure that the user is
+   * not left staring at an apparently hung, blank window.
+   */
+  HWND msg = GetDlgItem( (HWND)(window), IDD_PROGRESS_MSG );
+  HWND dlg = GetDlgItem( (HWND)(window), IDD_PROGRESS_BAR );
+  AppWindowMaker *app = GetAppWindow( GetParent( (HWND)(window) ));
+  ProgressMeterMaker ui( msg, dlg, app );
+
+  /* After setting up the progress meter, we clear out any data
+   * which was previously loaded into the package list, reload it
+   * with the "forced download" option, and refresh the display.
+   */
+  app->ClearPackageList();
+  app->LoadPackageData( true );
+  app->UpdatePackageList();
+
+  /* During the update, the user may have selected the option for
+   * automatic dismissal of the dialogue box on completion...
+   */
+  if( IsDlgButtonChecked( (HWND)(window), IDD_AUTO_CLOSE_OPTION ) )
+    /*
+     * ...in which case, we dismiss it without further ado...
+     */
+    SendMessage( (HWND)(window), WM_COMMAND, (WPARAM)(IDOK), 0 );
+
+  else
+  { /* ...otherwise, we activate the manual dismissal button...
+     */
+    if( (dlg = GetDlgItem( (HWND)(window), IDOK )) != NULL )
+      EnableWindow( dlg, TRUE );
+
+    /* ...and notify the user that it must be clicked to continue.
+     */
+    ui.Annotate( "Update is complete; please close this dialogue to continue." );
+  }
+}
+
+static int CALLBACK pkgUpdate
+( HWND window, unsigned int msg, WPARAM wParam, LPARAM lParam )
+{
+  /* Handler for the package catalogue update dialogue box, as
+   * invoked by the "Update catalogue" menu pick, (or equivalent
+   * tool-bar button selection).
+   */
+  switch( msg )
+  {
+    /* We need to handle only two classes of windows messages
+     * on behalf of this dialogue box...
+     */
+    case WM_INITDIALOG:
+      /*
+       * ...viz. on initial dialogue box creation, we delegate the actual
+       * activity, of updating the catalogue, to this background thread...
+       */
+      _beginthread( pkgInvokeUpdate, 0, (void *)(window) );
+      return TRUE;
+
+    case WM_COMMAND:
+      if( LOWORD( wParam ) == IDOK )
+      {
+       /* ...then we wait for a notification that the dialogue may be
+        * closed, (which isn't permitted until the thread completes).
+        */
+       EndDialog( window, 0 );
+       return TRUE;
+      }
+  }
+  /* Any other messages, which are directed to this dialogue box, may be
+   * safely ignored.
+   */
+  return FALSE;
+}
+
+int AppWindowMaker::Invoked( void )
+{
+  /* Override for the WTK::MainWindowMaker::Invoked() method; it
+   * provides the hook for the initial loading of the XML database,
+   * 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; this activity
+   * is invoked in a background thread, initiated from a progress
+   * dialogue derived from the "Update Catalogue" template.
+   */
+  DialogBox( AppInstance,
+      MAKEINTRESOURCE( IDD_REPO_UPDATE ), AppWindow, pkgInitDataLoad
+    );
+  InitPackageListView();
+
+  /* Initialise the data-sheet tab control, displaying the default
+   * "no package selected" message.
+   */
+  InitPackageTabControl();
+
+  /* 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();
+}
+
+long AppWindowMaker::OnCommand( WPARAM cmd )
+{
+  /* Handler for WM_COMMAND messages which are directed to the
+   * top level application window.
+   */
+  switch( cmd )
+  { case IDM_HELP_ABOUT:
+      /*
+       * This request is initiated by selecting "About mingw-get"
+       * from the "Help" menu; we respond by displaying the "about"
+       * dialogue box.
+       */
+      WTK::GenericDialogue( AppInstance, AppWindow, IDD_HELP_ABOUT );
+      break;
+
+    case IDM_REPO_UPDATE:
+      /*
+       * This request is initiated by selecting "Update Catalogue"
+       * from the "Repository" menu; we respond by initiating a progress
+       * dialogue, from which a background thread is invoked to download
+       * fresh copies of the package catalogue files from the remote
+       * repository, and consolidate them into the local catalogue.
+       */
+      DialogBox(
+         AppInstance, MAKEINTRESOURCE( IDD_REPO_UPDATE ), AppWindow, pkgUpdate
+       );
+      break;
+
+    case IDM_REPO_QUIT:
+      /*
+       * This request is initiated by selecting the "Quit" option
+       * from the "Repository" menu; we respond by sending a WM_QUIT
+       * message, to terminate the current application instance.
+       */
+      SendMessage( AppWindow, WM_CLOSE, 0, 0L );
+      break;
+  }
+  /* Any other message is silently ignored.
+   */
+  return EXIT_SUCCESS;
+}
+
 /* $RCSfile$: end of file */
index 833a9cc..4857d1e 100644 (file)
@@ -87,6 +87,26 @@ EXTERN_C int pkgPutEnv( int, char* );
 class pkgSpecs;
 class pkgDirectory;
 
+#ifndef GUIMAIN_H
+class AppWindowMaker;
+#endif
+
+class pkgProgressMeter
+{
+  /* An abstract base class, from which the controller class
+   * for a progress meter dialogue window may be derived.
+   */
+  public:
+    virtual void SetValue( int ) = 0;
+    virtual void SetRange( int, int ) = 0;
+    virtual int Annotate( const char *, ... ) = 0;
+
+  protected:
+    AppWindowMaker *referrer;
+    pkgProgressMeter( AppWindowMaker *ref = NULL ): referrer( ref ){}
+    ~pkgProgressMeter();
+};
+
 class pkgXmlNode : public TiXmlElement
 {
   /* A minimal emulation of the wxXmlNode class, founded on
@@ -326,8 +346,8 @@ class pkgXmlDocument : public TiXmlDocument
   public:
     /* Constructors...
      */
-    inline pkgXmlDocument(){}
-    inline pkgXmlDocument( const char* name )
+    inline pkgXmlDocument(): progress_meter( NULL ){}
+    inline pkgXmlDocument( const char* name ): progress_meter( NULL )
     {
       /* tinyxml has a similar constructor, but unlike wxXmlDocument,
        * it DOES NOT automatically load the document; force it.
@@ -448,6 +468,28 @@ class pkgXmlDocument : public TiXmlDocument
     {
       actions->GetScheduledSourceArchives( category );
     }
+
+  /* Facility for monitoring of XML document processing operations.
+   */
+  private:
+    pkgProgressMeter* progress_meter;
+
+  public:
+    inline pkgProgressMeter *ProgressMeter( void )
+    {
+      return progress_meter;
+    }
+    inline pkgProgressMeter *AttachProgressMeter( pkgProgressMeter *attachment )
+    {
+      if( progress_meter == NULL )
+       progress_meter = attachment;
+      return progress_meter;
+    }
+    inline void DetachProgressMeter( pkgProgressMeter *attachment )
+    {
+      if( attachment == progress_meter )
+       progress_meter = NULL;
+    }
 };
 
 EXTERN_C const char *xmlfile( const char*, const char* = NULL );
index f6066c6..5252668 100644 (file)
@@ -4,7 +4,7 @@
  * $Id$
  *
  * Written by Keith Marshall <keithmarshall@users.sourceforge.net>
- * Copyright (C) 2009, 2010, 2011, MinGW Project
+ * Copyright (C) 2009, 2010, 2011, 2012, MinGW.org Project
  *
  *
  * Implementation of repository binding for the pkgXmlDocument class.
@@ -40,6 +40,9 @@ class pkgRepository
    * of package lists, from any specified repository.
    */
   public:
+    static void Reset( void ){ count = total = 0; }
+    static void IncrementTotal( void ){ ++total; }
+
     pkgRepository( pkgXmlDocument*, pkgXmlNode*, pkgXmlNode*, bool );
     ~pkgRepository(){};
 
@@ -50,9 +53,16 @@ class pkgRepository
     pkgXmlNode *dbase;
     pkgXmlNode *repository;
     pkgXmlDocument *owner;
+    static int count, total;
     bool force_update;
 };
 
+/* Don't forget that we MUST explicitly allocate static storage for
+ * static property values declared within the pkgRepository class.
+ */
+int pkgRepository::count;
+int pkgRepository::total;
+
 pkgRepository::pkgRepository
 /*
  * Constructor...
@@ -76,14 +86,50 @@ void pkgRepository::GetPackageList( const char *dname )
     {
       /* Check for a locally cached copy of the "package-list" file...
        */
+      const char *mode = "Loading";
+      const char *fmt = "%s catalogue: %s.xml; (item %d of %d)\n";
       if( force_update || (access( dfile, F_OK ) != 0) )
       {
        /* When performing an "update", or if no local copy is available...
         * Force a "sync", to fetch a copy from the public host.
         */
-       dmh_printf( "Update catalogue: %s.xml\n", dname );
+       const char *mode = force_update ? "Updating" : "Downloading";
+       if( owner->ProgressMeter() != NULL )
+         /*
+          * Progress of the "update" is being metered; annotate the
+          * metering display accordingly...
+          */
+         owner->ProgressMeter()->Annotate( fmt, mode, dname, ++count, total );
+
+       else
+         /* Progress is not being explicitly metered, but the user
+          * may still appreciate a minimal progress report...
+          */
+         dmh_printf( fmt, mode, dname, ++count, total );
+
+       /* During the actual fetch, collect any generated diagnostics
+        * for the current catalogue file into a message digest, so
+        * that the GUI may present them in a single message box.
+        */
+       dmh_control( DMH_BEGIN_DIGEST );
        owner->SyncRepository( dname, repository );
       }
+      else if( owner->ProgressMeter() != NULL )
+       /*
+        * This is a simple request to load a local copy of the
+        * catalogue file; progress metering is in effect, so we
+        * annotate the metering display accordingly...
+        */
+       owner->ProgressMeter()->Annotate( fmt, mode, dname, ++count, total );
+
+      else if( pkgOptions()->Test( OPTION_VERBOSE ) > 1 )
+       /*
+        * Similarly, this is a request to load a local copy of
+        * the catalogue; progress metering is not in effect, but
+        * the user has requested verbose diagnostics, so issue
+        * a diagnostic progress report.
+        */
+       dmh_printf( fmt, mode, dname, ++count, total );
 
       /* We SHOULD now have a locally cached copy of the package-list;
        * attempt to merge it into the active profile database...
@@ -94,8 +140,6 @@ void pkgRepository::GetPackageList( const char *dname )
        /* We successfully loaded the XML catalogue; refer to its
         * root element...
         */
-       if( pkgOptions()->Test( OPTION_VERBOSE ) > 1 )
-         dmh_printf( "Load catalogue: %s.xml\n", dname );
        pkgXmlNode *catalogue, *pkglist;
        if( (catalogue = merge.GetRoot()) != NULL )
        {
@@ -118,7 +162,31 @@ void pkgRepository::GetPackageList( const char *dname )
          /* Recursively incorporate any additional package lists,
           * which may be specified within the current catalogue...
           */
-         GetPackageList( catalogue->FindFirstAssociate( package_list_key ) );
+         catalogue = catalogue->FindFirstAssociate( package_list_key );
+         if( (pkglist = catalogue) != NULL )
+           do {
+                /* ...updating the total catalogue reference count,
+                 * to include all extra catalogue files specified.
+                 */
+                ++total;
+                pkglist = pkglist->FindNextAssociate( package_list_key );
+              } while( pkglist != NULL );
+
+         /* Flush any message digest which has been accumulated
+          * for the last catalogue processed...
+          */
+         dmh_control( DMH_END_DIGEST );
+         if( owner->ProgressMeter() != NULL )
+         {
+           /* ...and update the progress meter display, if any,
+            * to reflect current progress.
+            */
+           owner->ProgressMeter()->SetRange( 0, total );
+           owner->ProgressMeter()->SetValue( count );
+         }
+         /* Proceed to process the embedded catalogues.
+          */
+         GetPackageList( catalogue );
        }
       }
       else
@@ -135,6 +203,10 @@ void pkgRepository::GetPackageList( const char *dname )
       free( (void *)(dfile) );
     }
   }
+  /* Ensure that any accumulated diagnostics, pertaining to catalogue
+   * processing, have been displayed before wrapping up.
+   */
+  dmh_control( DMH_END_DIGEST );
 }
 
 void pkgRepository::GetPackageList( pkgXmlNode *catalogue )
@@ -182,6 +254,7 @@ pkgXmlNode *pkgXmlDocument::BindRepositories( bool force_update )
     /* Sanity check passed...
      * Walk the XML data tree, selecting "repository" specifications...
      */
+    pkgRepository::Reset();
     pkgXmlNode *repository = dbase->FindFirstAssociate( repository_key );
     while( repository != NULL )
     {
@@ -190,18 +263,25 @@ pkgXmlNode *pkgXmlDocument::BindRepositories( bool force_update )
       pkgRepository client( this, dbase, repository, force_update );
       pkgXmlNode *catalogue = repository->FindFirstAssociate( package_list_key );
       if( catalogue == NULL )
-       /*
-        * This repository specification doesn't identify any named
+      {
+       /* This repository specification doesn't identify any named
         * package list, so try the default, (which is named to match
         * the XML key name for the "package-list" element)...
         */
+       pkgRepository::IncrementTotal();
        client.GetPackageList( package_list_key );
-
+      }
       else
-       /* At least one package list catalogue is specified; load it,
+      { /* At least one package list catalogue is specified; load it,
         * and any others which are explicitly identified...
         */
+       pkgXmlNode *ref = catalogue;
+       do { pkgRepository::IncrementTotal();
+            ref = ref->FindNextAssociate( package_list_key );
+          } while( ref != NULL );
+
        client.GetPackageList( catalogue );
+      }
 
       /* Similarly, a complete distribution may draw from an arbitrary set
        * of distinct repositories; move on, to process the next repository
index 25cb7de..335614f 100644 (file)
  *
  */
 #include "guimain.h"
+#include "pkgbase.h"
 #include "dmh.h"
 
-using WTK::GenericDialogue;
+using WTK::StringResource;
 using WTK::HorizontalSashWindowMaker;
 using WTK::VerticalSashWindowMaker;
-using WTK::StringResource;
 
 /* The main application window is divided into two
  * horizontally adjustable sash panes; the following
@@ -104,34 +104,6 @@ long AppWindowMaker::OnCreate()
   return EXIT_SUCCESS;
 }
 
-long AppWindowMaker::OnCommand( WPARAM cmd )
-{
-  /* Handler for WM_COMMAND messages which are directed to the
-   * top level application window.
-   */
-  switch( cmd )
-  {
-    case IDM_HELP_ABOUT:
-      /*
-       * This request is initiated by selecting "About mingw-get"
-       * from the "Help" menu; we respond by displaying the "about"
-       * dialogue box.
-       */
-      GenericDialogue( AppInstance, AppWindow, IDD_HELP_ABOUT );
-      break;
-
-    case IDM_REPO_QUIT:
-      /*
-       * This request is initiated by selecting the "Quit" option
-       * from the "Repository" menu; we respond by sending a WM_QUIT
-       * message, to terminate the current application instance.
-       */
-      SendMessage( AppWindow, WM_CLOSE, 0, 0L );
-      break;
-  }
-  return EXIT_SUCCESS;
-}
-
 #if 0
 /* FIXME: this stub implementation has been superseded by an
  * alternative implementation in pkgdata.cpp; eventually, this