From 75a80c32fc76bd51715cf803d1dd80f6bf7256e9 Mon Sep 17 00:00:00 2001 From: Keith Marshall Date: Sat, 27 Oct 2012 09:12:15 +0100 Subject: [PATCH] Initiate progress metering for catalogue load and update operations. --- ChangeLog | 50 ++++++++ src/guidata.rc | 18 ++- src/guimain.h | 22 +++- src/guixmld.cpp | 351 ++++++++++++++++++++++++++++++++++++++++++++++++++++---- src/pkgbase.h | 46 +++++++- src/pkgbind.cpp | 98 ++++++++++++++-- src/pkgview.cpp | 32 +----- 7 files changed, 548 insertions(+), 69 deletions(-) diff --git a/ChangeLog b/ChangeLog index 0d76564..f30ae95 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,53 @@ +2012-10-27 Keith Marshall + + 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 Associate DMH message boxes with active dialogues. diff --git a/src/guidata.rc b/src/guidata.rc index 49b5f5b..5c7888a 100644 --- a/src/guidata.rc +++ b/src/guidata.rc @@ -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 */ diff --git a/src/guimain.h b/src/guimain.h index 275e7c3..57f4c6f 100644 --- a/src/guimain.h +++ b/src/guimain.h @@ -80,6 +80,12 @@ #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 @@ -98,7 +104,9 @@ #include #include +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; diff --git a/src/guixmld.cpp b/src/guixmld.cpp index 08bcf0b..cf9112c 100644 --- a/src/guixmld.cpp +++ b/src/guixmld.cpp @@ -31,41 +31,98 @@ #include #include -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 */ diff --git a/src/pkgbase.h b/src/pkgbase.h index 833a9cc..4857d1e 100644 --- a/src/pkgbase.h +++ b/src/pkgbase.h @@ -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 ); diff --git a/src/pkgbind.cpp b/src/pkgbind.cpp index f6066c6..5252668 100644 --- a/src/pkgbind.cpp +++ b/src/pkgbind.cpp @@ -4,7 +4,7 @@ * $Id$ * * Written by Keith Marshall - * 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 diff --git a/src/pkgview.cpp b/src/pkgview.cpp index 25cb7de..335614f 100644 --- a/src/pkgview.cpp +++ b/src/pkgview.cpp @@ -26,12 +26,12 @@ * */ #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 -- 2.11.0