• R/O
  • HTTP
  • SSH
  • HTTPS

Commit

Tags
Keine Tags

Frequently used words (click to add to your profile)

javac++androidlinuxc#windowsobjective-ccocoa誰得qtpythonphprubygameguibathyscaphec計画中(planning stage)翻訳omegatframeworktwitterdomtestvb.netdirectxゲームエンジンbtronarduinopreviewer

The MinGW.OSDN Installation Manager Tool


Commit MetaInfo

Revision75a80c32fc76bd51715cf803d1dd80f6bf7256e9 (tree)
Zeit2012-10-27 17:12:15
AutorKeith Marshall <keithmarshall@user...>
CommiterKeith Marshall

Log Message

Initiate progress metering for catalogue load and update operations.

Ändern Zusammenfassung

Diff

--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,53 @@
1+2012-10-27 Keith Marshall <keithmarshall@users.sourceforge.net>
2+
3+ Initiate progress metering for catalogue load and update operations.
4+
5+ * src/guidata.rc (IDD_REPO_UPDATE): New dialogue template; define it.
6+ (IDM_REPO_UPDATE): Enable selection from "Repository" drop-down menu.
7+
8+ * src/guimain.h (IDD_REPO_UPDATE, IDD_CLOSE_OPTIONS):
9+ (IDD_AUTO_CLOSE_OPTION, IDD_PROGRESS_BAR, IDD_PROGRESS_MSG): New
10+ resource identification manifest constants; define them.
11+ (pkgXmlNode, pkgProgressMeter): Add forward class declarations.
12+ (AppWindowMaker::AttachedProgressMeter): New private data; define...
13+ (AppWindowMaker::AppWindowMaker): ...and initialise it.
14+ (AppWindowMaker::AttachProgressMeter): New public method; declare it.
15+ (AppWindowMaker::DetachProgressMeter): Likewise.
16+ (AppWindowMaker::LoadPackageData, AppWindowMaker::ClearPackageList):
17+ (AppWindowMaker::UpdatePackageList): Make them public.
18+
19+ * src/pkgbase.h (pkgProgressMeter): New abstract class; declare it.
20+ (AppWindowMaker) [GUIMAIN_H undefined]: Add forward class declaration.
21+ (pkgXmlDocument::progress_meter): New private data member; declare...
22+ (pkgXmlDocument::pkgXmlDocument): ...and initialise it.
23+ (pkgXmlDocument::ProgressMeter, pkgXmlDocument::AttachProgressMeter):
24+ (pkgXmlDocument::DetachProgressMeter): New public inline methods;
25+ implement them.
26+
27+ * src/pkgview.cpp (WTK::GenericDialogue): Delete disused reference.
28+ (AppWindowMaker::OnCommand): Factor out implementation; relocate it...
29+ * src/guixmld.cpp: ...to here; thus it may utilise dependants of...
30+ (ProgressMeterMaker): ...this new locally implemented class.
31+ (AppWindowMaker::AttachProgressMeter): Implement it.
32+ (AppWindowMaker::AttachProgressMeter): Likewise.
33+ (pkgProgressMeter::~pkgProgressMeter): Likewise.
34+ (pkgUpdate, pkgInvokeUpdate): New static functions; implement them.
35+ (AppWindowMaker::Invoked): Replace direct call to LoadPackageData by
36+ an indirect invocation, via a progress metering dialogue box using...
37+ (pkgInitDataLoad): ...this new static callback function, invoking...
38+ (pkgInvokeInitDataLoad): ...this new static thread function, whence...
39+ (AppWindowMaker::LoadPackageData): ...this; add hook-up call to...
40+ (pkgXmlDocument::AttachProgressMeter): ...incorporate this.
41+
42+ * src/pkgbind.cpp (pkgRepository::total, pkgRepository::count): New
43+ private static properties; declare and instantiate them; provide...
44+ (pkgRepository::Reset, pkgRepository::IncrementTotal): ...new public
45+ static methods to manipulate them; declare and implement them inline.
46+ (pkgRepository::GetPackageList): Use them to manage updates to the
47+ pkgProgressMeter class instance, if any, which has been bound to
48+ the controlling pkgXmlDocument class instance, when invoking...
49+ (pkgXmlXmlDocument::BindRepositories): ...this.
50+
151 2012-10-18 Keith Marshall <keithmarshall@users.sourceforge.net>
252
353 Associate DMH message boxes with active dialogues.
--- a/src/guidata.rc
+++ b/src/guidata.rc
@@ -73,7 +73,7 @@ BEGIN
7373 */
7474 POPUP "&Repository"
7575 BEGIN
76- MENUITEM "&Update Catalogue", IDM_REPO_UPDATE, GRAYED
76+ MENUITEM "&Update Catalogue", IDM_REPO_UPDATE
7777 MENUITEM SEPARATOR
7878 MENUITEM "&Quit\tAlt+F4", IDM_REPO_QUIT
7979 END
@@ -130,4 +130,20 @@ ID_PKGSTATE_BROKEN ICON DISCARDABLE "state11.ico"
130130 ID_PKGSTATE_REMOVE ICON DISCARDABLE "state12.ico"
131131 ID_PKGSTATE_PURGE ICON DISCARDABLE "state13.ico"
132132
133+/* Template for progress meter dialogue box.
134+ */
135+IDD_REPO_UPDATE DIALOG DISCARDABLE 10, 20, 270, 60
136+CAPTION "Update Package Catalogue"
137+STYLE DS_MODALFRAME | DS_SETFONT | WS_POPUP | WS_CAPTION | WS_DLGFRAME
138+FONT 10, "Verdana"
139+BEGIN
140+ GROUPBOX "Actions", IDD_CLOSE_OPTIONS, 5, 31, 260, 25
141+ DEFPUSHBUTTON "Close", IDOK, 219, 39, 40, 12, WS_GROUP | WS_DISABLED
142+ AUTOCHECKBOX "Close dialogue automatically, when update is complete.", \
143+ IDD_AUTO_CLOSE_OPTION, 10, 41, 200, 11
144+ CONTROL "", IDD_PROGRESS_BAR, PROGRESS_CLASS, WS_CHILD \
145+ | PBS_SMOOTH, 6, 20, 258, 10
146+ LTEXT "", IDD_PROGRESS_MSG, 7, 6, 256, 12
147+END
148+
133149 /* $RCSfile$: end of file */
--- a/src/guimain.h
+++ b/src/guimain.h
@@ -80,6 +80,12 @@
8080 #define IDM_HELP_ABOUT 603
8181 #define IDD_HELP_ABOUT 603
8282
83+#define IDD_REPO_UPDATE 610
84+#define IDD_CLOSE_OPTIONS 611
85+#define IDD_AUTO_CLOSE_OPTION 612
86+#define IDD_PROGRESS_BAR 613
87+#define IDD_PROGRESS_MSG 614
88+
8389 #define ID_PKGLIST_TABLE_HEADINGS 1024
8490 #define ID_PKGNAME_COLUMN_HEADING 1025
8591 #define ID_PKGTYPE_COLUMN_HEADING 1026
@@ -98,7 +104,9 @@
98104 #include <wtklite.h>
99105 #include <commctrl.h>
100106
107+class pkgXmlNode;
101108 class pkgXmlDocument;
109+class pkgProgressMeter;
102110 class DataSheetMaker;
103111
104112 class AppWindowMaker;
@@ -116,13 +124,21 @@ class AppWindowMaker: public WTK::MainWindowMaker
116124 {
117125 public:
118126 AppWindowMaker( HINSTANCE inst ): WTK::MainWindowMaker( inst ),
119- pkgData( NULL ), DefaultFont( (HFONT)(GetStockObject( DEFAULT_GUI_FONT )) ){}
127+ pkgData( NULL ), DefaultFont( (HFONT)(GetStockObject( DEFAULT_GUI_FONT )) ),
128+ AttachedProgressMeter( NULL ){}
120129 ~AppWindowMaker(){ /* delete ChildWindows; */ DeleteObject( DefaultFont ); }
121130
122131 HWND Create( const char *, const char * );
123132 inline long AdjustLayout( void ){ return OnSize( 0, 0, 0 ); }
124133 int Invoked( void );
125134
135+ void LoadPackageData( bool = false );
136+ void ClearPackageList( void ){ ListView_DeleteAllItems( PackageListView ); }
137+ void UpdatePackageList( void );
138+
139+ inline pkgProgressMeter *AttachProgressMeter( pkgProgressMeter * );
140+ inline void DetachProgressMeter( pkgProgressMeter * );
141+
126142 private:
127143 virtual long OnCreate();
128144 virtual long OnCommand( WPARAM );
@@ -134,13 +150,11 @@ class AppWindowMaker: public WTK::MainWindowMaker
134150 WTK::SashWindowMaker *HorizontalSash, *VerticalSash;
135151
136152 pkgXmlDocument *pkgData;
137- void LoadPackageData( bool = false );
153+ pkgProgressMeter *AttachedProgressMeter;
138154 HFONT DefaultFont;
139155
140156 HWND PackageListView;
141157 void InitPackageListView( void );
142- void ClearPackageList( void ){ ListView_DeleteAllItems( PackageListView ); }
143- void UpdatePackageList( void );
144158
145159 DataSheetMaker *DataSheet;
146160 WTK::ChildWindowMaker *TabDataPane;
--- a/src/guixmld.cpp
+++ b/src/guixmld.cpp
@@ -31,41 +31,98 @@
3131 #include <unistd.h>
3232 #include <wtkexcept.h>
3333
34-int AppWindowMaker::Invoked( void )
34+class ProgressMeterMaker: public pkgProgressMeter
3535 {
36- /* Override for the WTK::MainWindowMaker::Invoked() method; it
37- * provides the hook for the initial loading of the XML database,
38- * and creation of the display controls through which its content
39- * will be presented to the user, prior to invocation of the main
40- * window's message loop.
41- *
42- * The data displays depend on the MS-Windows Common Controls API;
43- * initialise all components of this up front.
36+ /* A locally defined class, supporting progress metering
37+ * for package catalogue update and load operations.
4438 */
45- InitCommonControls();
39+ public:
40+ ProgressMeterMaker( HWND, HWND, AppWindowMaker * );
41+
42+ virtual int Annotate( const char *, ... );
43+ virtual void SetRange( int, int );
44+ virtual void SetValue( int );
45+
46+ protected:
47+ HWND message_line, progress_bar;
48+};
4649
47- /* Load the data from the XML catalogue files, and construct the
48- * initial view of the available package list.
50+inline
51+pkgProgressMeter *AppWindowMaker::AttachProgressMeter( pkgProgressMeter *meter )
52+{
53+ /* A local helper method for attaching a progress meter to the
54+ * controlling class instance for the main application window.
4955 */
50- LoadPackageData();
51- InitPackageListView();
56+ if( AttachedProgressMeter == NULL )
57+ AttachedProgressMeter = meter;
58+ return AttachedProgressMeter;
59+}
5260
53- /* Initialise the data-sheet tab control, displaying the default
54- * "no package selected" message.
61+inline void AppWindowMaker::DetachProgressMeter( pkgProgressMeter *meter )
62+{
63+ /* A local helper method for detaching a progress meter from the
64+ * controlling class instance for the main application window.
5565 */
56- InitPackageTabControl();
66+ if( meter == AttachedProgressMeter )
67+ {
68+ pkgData->DetachProgressMeter( meter );
69+ AttachedProgressMeter = NULL;
70+ }
71+}
5772
58- /* Force a layout adjustment, to ensure that the displayed
59- * data controls are correctly populated.
73+/* We need to provide a destructor for the abstract base class, from which
74+ * our progress meters are derived; here is as good a place as any.
75+ */
76+pkgProgressMeter::~pkgProgressMeter(){ referrer->DetachProgressMeter( this ); }
77+
78+/* We must also provide the implementation of our local progress meter class.
79+ */
80+ProgressMeterMaker::ProgressMeterMaker
81+( HWND annotation, HWND indicator, AppWindowMaker *owner ):
82+pkgProgressMeter( owner ), message_line( annotation ), progress_bar( indicator )
83+{
84+ /* Constructor creates an instance of the progress meter class, attaching it
85+ * to the main application window, with an initial metering range of 0..100%,
86+ * and a starting indicated completion state of 0%.
6087 */
61- AdjustLayout();
88+ owner->AttachProgressMeter( this );
89+ SetRange( 0, 100 );
90+ SetValue( 0 );
91+}
6292
63- /* Finally, we may delegate all further processing to the main
64- * window's message loop.
93+int ProgressMeterMaker::Annotate( const char *fmt, ... )
94+{
95+ /* Method to add a printf() style annotation to the progress meter dialogue.
6596 */
66- return WTK::MainWindowMaker::Invoked();
97+ va_list argv;
98+ va_start( argv, fmt );
99+ char annotation[1 + vsnprintf( NULL, 0, fmt, argv )];
100+ int len = vsnprintf( annotation, sizeof( annotation ), fmt, argv );
101+ va_end( argv );
102+
103+ SendMessage( message_line, WM_SETTEXT, 0, (LPARAM)(annotation) );
104+ return len;
67105 }
68106
107+void ProgressMeterMaker::SetRange( int min, int max )
108+{
109+ /* Method to adjust the range of the progress meter, to represent any
110+ * arbitrary range of discrete values, rather than percentage units.
111+ */
112+ SendMessage( progress_bar, PBM_SETRANGE, 0, MAKELPARAM( min, max ) );
113+}
114+
115+void ProgressMeterMaker::SetValue( int value )
116+{
117+ /* Method to update the indicated completion state of a progress meter,
118+ * to represent any arbitrary value within its assigned metering range.
119+ */
120+ SendMessage( progress_bar, PBM_SETPOS, value, 0 );
121+}
122+
123+/* Implementation of service routines, for loading the package catalogue
124+ * from its defining collection of XML files.
125+ */
69126 void AppWindowMaker::LoadPackageData( bool force_update )
70127 {
71128 /* Helper method to load the package database from its
@@ -119,6 +176,7 @@ void AppWindowMaker::LoadPackageData( bool force_update )
119176 /* Establish the repository URI references, for retrieval
120177 * of the downloadable catalogue files, and load them...
121178 */
179+ pkgData->AttachProgressMeter( AttachedProgressMeter );
122180 if( pkgData->BindRepositories( force_update ) == NULL )
123181 /*
124182 * ...once again, bailing out on failure.
@@ -131,4 +189,251 @@ void AppWindowMaker::LoadPackageData( bool force_update )
131189 pkgData->LoadSystemMap();
132190 }
133191
192+static void pkgInvokeInitDataLoad( void *window )
193+{
194+ /* Thread procedure for performing the initial catalogue load, on
195+ * application start-up. This will load from locally cached data
196+ * files, when available; however, it will also initiate a download
197+ * from the remote repository, for any file which is missing from
198+ * the local cache. Since this may be a time consuming process,
199+ * we subject it to progress metering, to ensure that the user is
200+ * not left staring at an apparently hung, blank window.
201+ */
202+ HWND msg = GetDlgItem( (HWND)(window), IDD_PROGRESS_MSG );
203+ HWND dlg = GetDlgItem( (HWND)(window), IDD_PROGRESS_BAR );
204+ AppWindowMaker *app = GetAppWindow( GetParent( (HWND)(window) ));
205+ SendMessage( (HWND)(window),
206+ WM_SETTEXT, 0, (LPARAM)("Loading Package Catalogue")
207+ );
208+ ProgressMeterMaker ui( msg, dlg, app );
209+
210+ /* For this activity, we request automatic dismissal of the dialogue,
211+ * when loading has been completed; the user will have an opportunity
212+ * to countermand this choice, if loading is delayed by the required
213+ * download of any missing local catalogue file.
214+ */
215+ dlg = GetDlgItem( (HWND)(window), IDD_AUTO_CLOSE_OPTION );
216+ SendMessage( dlg, WM_SETTEXT, 0,
217+ (LPARAM)("Close dialogue automatically, when loading is complete.")
218+ );
219+ CheckDlgButton( (HWND)(window), IDD_AUTO_CLOSE_OPTION, BST_CHECKED );
220+
221+ /* We've now set up the initial state for the progress meter dialogue;
222+ * proceed to load, (and perhaps download), the XML data files.
223+ */
224+ app->LoadPackageData( false );
225+
226+ /* When loading has been completed, automatically dismiss the dialogue...
227+ */
228+ if( IsDlgButtonChecked( (HWND)(window), IDD_AUTO_CLOSE_OPTION ) )
229+ SendMessage( (HWND)(window), WM_COMMAND, (WPARAM)(IDOK), 0 );
230+
231+ /* ...unless the user has countermanded the automatic dismissal request...
232+ */
233+ else
234+ { /* ...in which case, we activate the manual dismissal button...
235+ */
236+ if( (dlg = GetDlgItem( (HWND)(window), IDOK )) != NULL )
237+ EnableWindow( dlg, TRUE );
238+
239+ /* ...and notify the user that it must be clicked to continue.
240+ */
241+ ui.Annotate( "Data has been loaded; please close this dialogue to continue." );
242+ }
243+}
244+
245+static int CALLBACK pkgInitDataLoad
246+( HWND window, unsigned int msg, WPARAM wParam, LPARAM lParam )
247+{
248+ /* Handler for the initial catalogue loading progress dialogue.
249+ */
250+ switch( msg )
251+ {
252+ /* We need to handle only two classes of windows messages
253+ * on behalf of this dialogue box...
254+ */
255+ case WM_INITDIALOG:
256+ /*
257+ * ...viz. on initial dialogue box creation, we delegate the actual
258+ * activity, of loading the catalogue, to this background thread...
259+ */
260+ _beginthread( pkgInvokeInitDataLoad, 0, (void *)(window) );
261+ return TRUE;
262+
263+ case WM_COMMAND:
264+ if( LOWORD( wParam ) == IDOK )
265+ {
266+ /* ...then we wait for a notification that the dialogue may be
267+ * closed, (which isn't permitted until the thread completes).
268+ */
269+ EndDialog( window, 0 );
270+ return TRUE;
271+ }
272+ }
273+ /* Any other messages, which are directed to this dialogue box, may be
274+ * safely ignored.
275+ */
276+ return FALSE;
277+}
278+
279+static void pkgInvokeUpdate( void *window )
280+{
281+ /* Thread procedure for performing a package catalogue update.
282+ * This will download catalogue files from the remote repository,
283+ * and integrate them into the locally cached catalogue XML file
284+ * set. Since this is normally a time consuming process, we must
285+ * subject it to progress metering, to ensure that the user is
286+ * not left staring at an apparently hung, blank window.
287+ */
288+ HWND msg = GetDlgItem( (HWND)(window), IDD_PROGRESS_MSG );
289+ HWND dlg = GetDlgItem( (HWND)(window), IDD_PROGRESS_BAR );
290+ AppWindowMaker *app = GetAppWindow( GetParent( (HWND)(window) ));
291+ ProgressMeterMaker ui( msg, dlg, app );
292+
293+ /* After setting up the progress meter, we clear out any data
294+ * which was previously loaded into the package list, reload it
295+ * with the "forced download" option, and refresh the display.
296+ */
297+ app->ClearPackageList();
298+ app->LoadPackageData( true );
299+ app->UpdatePackageList();
300+
301+ /* During the update, the user may have selected the option for
302+ * automatic dismissal of the dialogue box on completion...
303+ */
304+ if( IsDlgButtonChecked( (HWND)(window), IDD_AUTO_CLOSE_OPTION ) )
305+ /*
306+ * ...in which case, we dismiss it without further ado...
307+ */
308+ SendMessage( (HWND)(window), WM_COMMAND, (WPARAM)(IDOK), 0 );
309+
310+ else
311+ { /* ...otherwise, we activate the manual dismissal button...
312+ */
313+ if( (dlg = GetDlgItem( (HWND)(window), IDOK )) != NULL )
314+ EnableWindow( dlg, TRUE );
315+
316+ /* ...and notify the user that it must be clicked to continue.
317+ */
318+ ui.Annotate( "Update is complete; please close this dialogue to continue." );
319+ }
320+}
321+
322+static int CALLBACK pkgUpdate
323+( HWND window, unsigned int msg, WPARAM wParam, LPARAM lParam )
324+{
325+ /* Handler for the package catalogue update dialogue box, as
326+ * invoked by the "Update catalogue" menu pick, (or equivalent
327+ * tool-bar button selection).
328+ */
329+ switch( msg )
330+ {
331+ /* We need to handle only two classes of windows messages
332+ * on behalf of this dialogue box...
333+ */
334+ case WM_INITDIALOG:
335+ /*
336+ * ...viz. on initial dialogue box creation, we delegate the actual
337+ * activity, of updating the catalogue, to this background thread...
338+ */
339+ _beginthread( pkgInvokeUpdate, 0, (void *)(window) );
340+ return TRUE;
341+
342+ case WM_COMMAND:
343+ if( LOWORD( wParam ) == IDOK )
344+ {
345+ /* ...then we wait for a notification that the dialogue may be
346+ * closed, (which isn't permitted until the thread completes).
347+ */
348+ EndDialog( window, 0 );
349+ return TRUE;
350+ }
351+ }
352+ /* Any other messages, which are directed to this dialogue box, may be
353+ * safely ignored.
354+ */
355+ return FALSE;
356+}
357+
358+int AppWindowMaker::Invoked( void )
359+{
360+ /* Override for the WTK::MainWindowMaker::Invoked() method; it
361+ * provides the hook for the initial loading of the XML database,
362+ * and creation of the display controls through which its content
363+ * will be presented to the user, prior to invocation of the main
364+ * window's message loop.
365+ *
366+ * The data displays depend on the MS-Windows Common Controls API;
367+ * initialise all components of this up front.
368+ */
369+ InitCommonControls();
370+
371+ /* Load the data from the XML catalogue files, and construct
372+ * the initial view of the available package list; this activity
373+ * is invoked in a background thread, initiated from a progress
374+ * dialogue derived from the "Update Catalogue" template.
375+ */
376+ DialogBox( AppInstance,
377+ MAKEINTRESOURCE( IDD_REPO_UPDATE ), AppWindow, pkgInitDataLoad
378+ );
379+ InitPackageListView();
380+
381+ /* Initialise the data-sheet tab control, displaying the default
382+ * "no package selected" message.
383+ */
384+ InitPackageTabControl();
385+
386+ /* Force a layout adjustment, to ensure that the displayed
387+ * data controls are correctly populated.
388+ */
389+ AdjustLayout();
390+
391+ /* Finally, we may delegate all further processing to the main
392+ * window's message loop.
393+ */
394+ return WTK::MainWindowMaker::Invoked();
395+}
396+
397+long AppWindowMaker::OnCommand( WPARAM cmd )
398+{
399+ /* Handler for WM_COMMAND messages which are directed to the
400+ * top level application window.
401+ */
402+ switch( cmd )
403+ { case IDM_HELP_ABOUT:
404+ /*
405+ * This request is initiated by selecting "About mingw-get"
406+ * from the "Help" menu; we respond by displaying the "about"
407+ * dialogue box.
408+ */
409+ WTK::GenericDialogue( AppInstance, AppWindow, IDD_HELP_ABOUT );
410+ break;
411+
412+ case IDM_REPO_UPDATE:
413+ /*
414+ * This request is initiated by selecting "Update Catalogue"
415+ * from the "Repository" menu; we respond by initiating a progress
416+ * dialogue, from which a background thread is invoked to download
417+ * fresh copies of the package catalogue files from the remote
418+ * repository, and consolidate them into the local catalogue.
419+ */
420+ DialogBox(
421+ AppInstance, MAKEINTRESOURCE( IDD_REPO_UPDATE ), AppWindow, pkgUpdate
422+ );
423+ break;
424+
425+ case IDM_REPO_QUIT:
426+ /*
427+ * This request is initiated by selecting the "Quit" option
428+ * from the "Repository" menu; we respond by sending a WM_QUIT
429+ * message, to terminate the current application instance.
430+ */
431+ SendMessage( AppWindow, WM_CLOSE, 0, 0L );
432+ break;
433+ }
434+ /* Any other message is silently ignored.
435+ */
436+ return EXIT_SUCCESS;
437+}
438+
134439 /* $RCSfile$: end of file */
--- a/src/pkgbase.h
+++ b/src/pkgbase.h
@@ -87,6 +87,26 @@ EXTERN_C int pkgPutEnv( int, char* );
8787 class pkgSpecs;
8888 class pkgDirectory;
8989
90+#ifndef GUIMAIN_H
91+class AppWindowMaker;
92+#endif
93+
94+class pkgProgressMeter
95+{
96+ /* An abstract base class, from which the controller class
97+ * for a progress meter dialogue window may be derived.
98+ */
99+ public:
100+ virtual void SetValue( int ) = 0;
101+ virtual void SetRange( int, int ) = 0;
102+ virtual int Annotate( const char *, ... ) = 0;
103+
104+ protected:
105+ AppWindowMaker *referrer;
106+ pkgProgressMeter( AppWindowMaker *ref = NULL ): referrer( ref ){}
107+ ~pkgProgressMeter();
108+};
109+
90110 class pkgXmlNode : public TiXmlElement
91111 {
92112 /* A minimal emulation of the wxXmlNode class, founded on
@@ -326,8 +346,8 @@ class pkgXmlDocument : public TiXmlDocument
326346 public:
327347 /* Constructors...
328348 */
329- inline pkgXmlDocument(){}
330- inline pkgXmlDocument( const char* name )
349+ inline pkgXmlDocument(): progress_meter( NULL ){}
350+ inline pkgXmlDocument( const char* name ): progress_meter( NULL )
331351 {
332352 /* tinyxml has a similar constructor, but unlike wxXmlDocument,
333353 * it DOES NOT automatically load the document; force it.
@@ -448,6 +468,28 @@ class pkgXmlDocument : public TiXmlDocument
448468 {
449469 actions->GetScheduledSourceArchives( category );
450470 }
471+
472+ /* Facility for monitoring of XML document processing operations.
473+ */
474+ private:
475+ pkgProgressMeter* progress_meter;
476+
477+ public:
478+ inline pkgProgressMeter *ProgressMeter( void )
479+ {
480+ return progress_meter;
481+ }
482+ inline pkgProgressMeter *AttachProgressMeter( pkgProgressMeter *attachment )
483+ {
484+ if( progress_meter == NULL )
485+ progress_meter = attachment;
486+ return progress_meter;
487+ }
488+ inline void DetachProgressMeter( pkgProgressMeter *attachment )
489+ {
490+ if( attachment == progress_meter )
491+ progress_meter = NULL;
492+ }
451493 };
452494
453495 EXTERN_C const char *xmlfile( const char*, const char* = NULL );
--- a/src/pkgbind.cpp
+++ b/src/pkgbind.cpp
@@ -4,7 +4,7 @@
44 * $Id$
55 *
66 * Written by Keith Marshall <keithmarshall@users.sourceforge.net>
7- * Copyright (C) 2009, 2010, 2011, MinGW Project
7+ * Copyright (C) 2009, 2010, 2011, 2012, MinGW.org Project
88 *
99 *
1010 * Implementation of repository binding for the pkgXmlDocument class.
@@ -40,6 +40,9 @@ class pkgRepository
4040 * of package lists, from any specified repository.
4141 */
4242 public:
43+ static void Reset( void ){ count = total = 0; }
44+ static void IncrementTotal( void ){ ++total; }
45+
4346 pkgRepository( pkgXmlDocument*, pkgXmlNode*, pkgXmlNode*, bool );
4447 ~pkgRepository(){};
4548
@@ -50,9 +53,16 @@ class pkgRepository
5053 pkgXmlNode *dbase;
5154 pkgXmlNode *repository;
5255 pkgXmlDocument *owner;
56+ static int count, total;
5357 bool force_update;
5458 };
5559
60+/* Don't forget that we MUST explicitly allocate static storage for
61+ * static property values declared within the pkgRepository class.
62+ */
63+int pkgRepository::count;
64+int pkgRepository::total;
65+
5666 pkgRepository::pkgRepository
5767 /*
5868 * Constructor...
@@ -76,14 +86,50 @@ void pkgRepository::GetPackageList( const char *dname )
7686 {
7787 /* Check for a locally cached copy of the "package-list" file...
7888 */
89+ const char *mode = "Loading";
90+ const char *fmt = "%s catalogue: %s.xml; (item %d of %d)\n";
7991 if( force_update || (access( dfile, F_OK ) != 0) )
8092 {
8193 /* When performing an "update", or if no local copy is available...
8294 * Force a "sync", to fetch a copy from the public host.
8395 */
84- dmh_printf( "Update catalogue: %s.xml\n", dname );
96+ const char *mode = force_update ? "Updating" : "Downloading";
97+ if( owner->ProgressMeter() != NULL )
98+ /*
99+ * Progress of the "update" is being metered; annotate the
100+ * metering display accordingly...
101+ */
102+ owner->ProgressMeter()->Annotate( fmt, mode, dname, ++count, total );
103+
104+ else
105+ /* Progress is not being explicitly metered, but the user
106+ * may still appreciate a minimal progress report...
107+ */
108+ dmh_printf( fmt, mode, dname, ++count, total );
109+
110+ /* During the actual fetch, collect any generated diagnostics
111+ * for the current catalogue file into a message digest, so
112+ * that the GUI may present them in a single message box.
113+ */
114+ dmh_control( DMH_BEGIN_DIGEST );
85115 owner->SyncRepository( dname, repository );
86116 }
117+ else if( owner->ProgressMeter() != NULL )
118+ /*
119+ * This is a simple request to load a local copy of the
120+ * catalogue file; progress metering is in effect, so we
121+ * annotate the metering display accordingly...
122+ */
123+ owner->ProgressMeter()->Annotate( fmt, mode, dname, ++count, total );
124+
125+ else if( pkgOptions()->Test( OPTION_VERBOSE ) > 1 )
126+ /*
127+ * Similarly, this is a request to load a local copy of
128+ * the catalogue; progress metering is not in effect, but
129+ * the user has requested verbose diagnostics, so issue
130+ * a diagnostic progress report.
131+ */
132+ dmh_printf( fmt, mode, dname, ++count, total );
87133
88134 /* We SHOULD now have a locally cached copy of the package-list;
89135 * attempt to merge it into the active profile database...
@@ -94,8 +140,6 @@ void pkgRepository::GetPackageList( const char *dname )
94140 /* We successfully loaded the XML catalogue; refer to its
95141 * root element...
96142 */
97- if( pkgOptions()->Test( OPTION_VERBOSE ) > 1 )
98- dmh_printf( "Load catalogue: %s.xml\n", dname );
99143 pkgXmlNode *catalogue, *pkglist;
100144 if( (catalogue = merge.GetRoot()) != NULL )
101145 {
@@ -118,7 +162,31 @@ void pkgRepository::GetPackageList( const char *dname )
118162 /* Recursively incorporate any additional package lists,
119163 * which may be specified within the current catalogue...
120164 */
121- GetPackageList( catalogue->FindFirstAssociate( package_list_key ) );
165+ catalogue = catalogue->FindFirstAssociate( package_list_key );
166+ if( (pkglist = catalogue) != NULL )
167+ do {
168+ /* ...updating the total catalogue reference count,
169+ * to include all extra catalogue files specified.
170+ */
171+ ++total;
172+ pkglist = pkglist->FindNextAssociate( package_list_key );
173+ } while( pkglist != NULL );
174+
175+ /* Flush any message digest which has been accumulated
176+ * for the last catalogue processed...
177+ */
178+ dmh_control( DMH_END_DIGEST );
179+ if( owner->ProgressMeter() != NULL )
180+ {
181+ /* ...and update the progress meter display, if any,
182+ * to reflect current progress.
183+ */
184+ owner->ProgressMeter()->SetRange( 0, total );
185+ owner->ProgressMeter()->SetValue( count );
186+ }
187+ /* Proceed to process the embedded catalogues.
188+ */
189+ GetPackageList( catalogue );
122190 }
123191 }
124192 else
@@ -135,6 +203,10 @@ void pkgRepository::GetPackageList( const char *dname )
135203 free( (void *)(dfile) );
136204 }
137205 }
206+ /* Ensure that any accumulated diagnostics, pertaining to catalogue
207+ * processing, have been displayed before wrapping up.
208+ */
209+ dmh_control( DMH_END_DIGEST );
138210 }
139211
140212 void pkgRepository::GetPackageList( pkgXmlNode *catalogue )
@@ -182,6 +254,7 @@ pkgXmlNode *pkgXmlDocument::BindRepositories( bool force_update )
182254 /* Sanity check passed...
183255 * Walk the XML data tree, selecting "repository" specifications...
184256 */
257+ pkgRepository::Reset();
185258 pkgXmlNode *repository = dbase->FindFirstAssociate( repository_key );
186259 while( repository != NULL )
187260 {
@@ -190,18 +263,25 @@ pkgXmlNode *pkgXmlDocument::BindRepositories( bool force_update )
190263 pkgRepository client( this, dbase, repository, force_update );
191264 pkgXmlNode *catalogue = repository->FindFirstAssociate( package_list_key );
192265 if( catalogue == NULL )
193- /*
194- * This repository specification doesn't identify any named
266+ {
267+ /* This repository specification doesn't identify any named
195268 * package list, so try the default, (which is named to match
196269 * the XML key name for the "package-list" element)...
197270 */
271+ pkgRepository::IncrementTotal();
198272 client.GetPackageList( package_list_key );
199-
273+ }
200274 else
201- /* At least one package list catalogue is specified; load it,
275+ { /* At least one package list catalogue is specified; load it,
202276 * and any others which are explicitly identified...
203277 */
278+ pkgXmlNode *ref = catalogue;
279+ do { pkgRepository::IncrementTotal();
280+ ref = ref->FindNextAssociate( package_list_key );
281+ } while( ref != NULL );
282+
204283 client.GetPackageList( catalogue );
284+ }
205285
206286 /* Similarly, a complete distribution may draw from an arbitrary set
207287 * of distinct repositories; move on, to process the next repository
--- a/src/pkgview.cpp
+++ b/src/pkgview.cpp
@@ -26,12 +26,12 @@
2626 *
2727 */
2828 #include "guimain.h"
29+#include "pkgbase.h"
2930 #include "dmh.h"
3031
31-using WTK::GenericDialogue;
32+using WTK::StringResource;
3233 using WTK::HorizontalSashWindowMaker;
3334 using WTK::VerticalSashWindowMaker;
34-using WTK::StringResource;
3535
3636 /* The main application window is divided into two
3737 * horizontally adjustable sash panes; the following
@@ -104,34 +104,6 @@ long AppWindowMaker::OnCreate()
104104 return EXIT_SUCCESS;
105105 }
106106
107-long AppWindowMaker::OnCommand( WPARAM cmd )
108-{
109- /* Handler for WM_COMMAND messages which are directed to the
110- * top level application window.
111- */
112- switch( cmd )
113- {
114- case IDM_HELP_ABOUT:
115- /*
116- * This request is initiated by selecting "About mingw-get"
117- * from the "Help" menu; we respond by displaying the "about"
118- * dialogue box.
119- */
120- GenericDialogue( AppInstance, AppWindow, IDD_HELP_ABOUT );
121- break;
122-
123- case IDM_REPO_QUIT:
124- /*
125- * This request is initiated by selecting the "Quit" option
126- * from the "Repository" menu; we respond by sending a WM_QUIT
127- * message, to terminate the current application instance.
128- */
129- SendMessage( AppWindow, WM_CLOSE, 0, 0L );
130- break;
131- }
132- return EXIT_SUCCESS;
133-}
134-
135107 #if 0
136108 /* FIXME: this stub implementation has been superseded by an
137109 * alternative implementation in pkgdata.cpp; eventually, this