The MinGW.OSDN Installation Manager Tool
Revision | 75a80c32fc76bd51715cf803d1dd80f6bf7256e9 (tree) |
---|---|
Zeit | 2012-10-27 17:12:15 |
Autor | Keith Marshall <keithmarshall@user...> |
Commiter | Keith Marshall |
Initiate progress metering for catalogue load and update operations.
@@ -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 | + | |
1 | 51 | 2012-10-18 Keith Marshall <keithmarshall@users.sourceforge.net> |
2 | 52 | |
3 | 53 | Associate DMH message boxes with active dialogues. |
@@ -73,7 +73,7 @@ BEGIN | ||
73 | 73 | */ |
74 | 74 | POPUP "&Repository" |
75 | 75 | BEGIN |
76 | - MENUITEM "&Update Catalogue", IDM_REPO_UPDATE, GRAYED | |
76 | + MENUITEM "&Update Catalogue", IDM_REPO_UPDATE | |
77 | 77 | MENUITEM SEPARATOR |
78 | 78 | MENUITEM "&Quit\tAlt+F4", IDM_REPO_QUIT |
79 | 79 | END |
@@ -130,4 +130,20 @@ ID_PKGSTATE_BROKEN ICON DISCARDABLE "state11.ico" | ||
130 | 130 | ID_PKGSTATE_REMOVE ICON DISCARDABLE "state12.ico" |
131 | 131 | ID_PKGSTATE_PURGE ICON DISCARDABLE "state13.ico" |
132 | 132 | |
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 | + | |
133 | 149 | /* $RCSfile$: end of file */ |
@@ -80,6 +80,12 @@ | ||
80 | 80 | #define IDM_HELP_ABOUT 603 |
81 | 81 | #define IDD_HELP_ABOUT 603 |
82 | 82 | |
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 | + | |
83 | 89 | #define ID_PKGLIST_TABLE_HEADINGS 1024 |
84 | 90 | #define ID_PKGNAME_COLUMN_HEADING 1025 |
85 | 91 | #define ID_PKGTYPE_COLUMN_HEADING 1026 |
@@ -98,7 +104,9 @@ | ||
98 | 104 | #include <wtklite.h> |
99 | 105 | #include <commctrl.h> |
100 | 106 | |
107 | +class pkgXmlNode; | |
101 | 108 | class pkgXmlDocument; |
109 | +class pkgProgressMeter; | |
102 | 110 | class DataSheetMaker; |
103 | 111 | |
104 | 112 | class AppWindowMaker; |
@@ -116,13 +124,21 @@ class AppWindowMaker: public WTK::MainWindowMaker | ||
116 | 124 | { |
117 | 125 | public: |
118 | 126 | 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 ){} | |
120 | 129 | ~AppWindowMaker(){ /* delete ChildWindows; */ DeleteObject( DefaultFont ); } |
121 | 130 | |
122 | 131 | HWND Create( const char *, const char * ); |
123 | 132 | inline long AdjustLayout( void ){ return OnSize( 0, 0, 0 ); } |
124 | 133 | int Invoked( void ); |
125 | 134 | |
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 | + | |
126 | 142 | private: |
127 | 143 | virtual long OnCreate(); |
128 | 144 | virtual long OnCommand( WPARAM ); |
@@ -134,13 +150,11 @@ class AppWindowMaker: public WTK::MainWindowMaker | ||
134 | 150 | WTK::SashWindowMaker *HorizontalSash, *VerticalSash; |
135 | 151 | |
136 | 152 | pkgXmlDocument *pkgData; |
137 | - void LoadPackageData( bool = false ); | |
153 | + pkgProgressMeter *AttachedProgressMeter; | |
138 | 154 | HFONT DefaultFont; |
139 | 155 | |
140 | 156 | HWND PackageListView; |
141 | 157 | void InitPackageListView( void ); |
142 | - void ClearPackageList( void ){ ListView_DeleteAllItems( PackageListView ); } | |
143 | - void UpdatePackageList( void ); | |
144 | 158 | |
145 | 159 | DataSheetMaker *DataSheet; |
146 | 160 | WTK::ChildWindowMaker *TabDataPane; |
@@ -31,41 +31,98 @@ | ||
31 | 31 | #include <unistd.h> |
32 | 32 | #include <wtkexcept.h> |
33 | 33 | |
34 | -int AppWindowMaker::Invoked( void ) | |
34 | +class ProgressMeterMaker: public pkgProgressMeter | |
35 | 35 | { |
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. | |
44 | 38 | */ |
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 | +}; | |
46 | 49 | |
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. | |
49 | 55 | */ |
50 | - LoadPackageData(); | |
51 | - InitPackageListView(); | |
56 | + if( AttachedProgressMeter == NULL ) | |
57 | + AttachedProgressMeter = meter; | |
58 | + return AttachedProgressMeter; | |
59 | +} | |
52 | 60 | |
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. | |
55 | 65 | */ |
56 | - InitPackageTabControl(); | |
66 | + if( meter == AttachedProgressMeter ) | |
67 | + { | |
68 | + pkgData->DetachProgressMeter( meter ); | |
69 | + AttachedProgressMeter = NULL; | |
70 | + } | |
71 | +} | |
57 | 72 | |
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%. | |
60 | 87 | */ |
61 | - AdjustLayout(); | |
88 | + owner->AttachProgressMeter( this ); | |
89 | + SetRange( 0, 100 ); | |
90 | + SetValue( 0 ); | |
91 | +} | |
62 | 92 | |
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. | |
65 | 96 | */ |
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; | |
67 | 105 | } |
68 | 106 | |
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 | + */ | |
69 | 126 | void AppWindowMaker::LoadPackageData( bool force_update ) |
70 | 127 | { |
71 | 128 | /* Helper method to load the package database from its |
@@ -119,6 +176,7 @@ void AppWindowMaker::LoadPackageData( bool force_update ) | ||
119 | 176 | /* Establish the repository URI references, for retrieval |
120 | 177 | * of the downloadable catalogue files, and load them... |
121 | 178 | */ |
179 | + pkgData->AttachProgressMeter( AttachedProgressMeter ); | |
122 | 180 | if( pkgData->BindRepositories( force_update ) == NULL ) |
123 | 181 | /* |
124 | 182 | * ...once again, bailing out on failure. |
@@ -131,4 +189,251 @@ void AppWindowMaker::LoadPackageData( bool force_update ) | ||
131 | 189 | pkgData->LoadSystemMap(); |
132 | 190 | } |
133 | 191 | |
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 | + | |
134 | 439 | /* $RCSfile$: end of file */ |
@@ -87,6 +87,26 @@ EXTERN_C int pkgPutEnv( int, char* ); | ||
87 | 87 | class pkgSpecs; |
88 | 88 | class pkgDirectory; |
89 | 89 | |
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 | + | |
90 | 110 | class pkgXmlNode : public TiXmlElement |
91 | 111 | { |
92 | 112 | /* A minimal emulation of the wxXmlNode class, founded on |
@@ -326,8 +346,8 @@ class pkgXmlDocument : public TiXmlDocument | ||
326 | 346 | public: |
327 | 347 | /* Constructors... |
328 | 348 | */ |
329 | - inline pkgXmlDocument(){} | |
330 | - inline pkgXmlDocument( const char* name ) | |
349 | + inline pkgXmlDocument(): progress_meter( NULL ){} | |
350 | + inline pkgXmlDocument( const char* name ): progress_meter( NULL ) | |
331 | 351 | { |
332 | 352 | /* tinyxml has a similar constructor, but unlike wxXmlDocument, |
333 | 353 | * it DOES NOT automatically load the document; force it. |
@@ -448,6 +468,28 @@ class pkgXmlDocument : public TiXmlDocument | ||
448 | 468 | { |
449 | 469 | actions->GetScheduledSourceArchives( category ); |
450 | 470 | } |
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 | + } | |
451 | 493 | }; |
452 | 494 | |
453 | 495 | EXTERN_C const char *xmlfile( const char*, const char* = NULL ); |
@@ -4,7 +4,7 @@ | ||
4 | 4 | * $Id$ |
5 | 5 | * |
6 | 6 | * 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 | |
8 | 8 | * |
9 | 9 | * |
10 | 10 | * Implementation of repository binding for the pkgXmlDocument class. |
@@ -40,6 +40,9 @@ class pkgRepository | ||
40 | 40 | * of package lists, from any specified repository. |
41 | 41 | */ |
42 | 42 | public: |
43 | + static void Reset( void ){ count = total = 0; } | |
44 | + static void IncrementTotal( void ){ ++total; } | |
45 | + | |
43 | 46 | pkgRepository( pkgXmlDocument*, pkgXmlNode*, pkgXmlNode*, bool ); |
44 | 47 | ~pkgRepository(){}; |
45 | 48 |
@@ -50,9 +53,16 @@ class pkgRepository | ||
50 | 53 | pkgXmlNode *dbase; |
51 | 54 | pkgXmlNode *repository; |
52 | 55 | pkgXmlDocument *owner; |
56 | + static int count, total; | |
53 | 57 | bool force_update; |
54 | 58 | }; |
55 | 59 | |
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 | + | |
56 | 66 | pkgRepository::pkgRepository |
57 | 67 | /* |
58 | 68 | * Constructor... |
@@ -76,14 +86,50 @@ void pkgRepository::GetPackageList( const char *dname ) | ||
76 | 86 | { |
77 | 87 | /* Check for a locally cached copy of the "package-list" file... |
78 | 88 | */ |
89 | + const char *mode = "Loading"; | |
90 | + const char *fmt = "%s catalogue: %s.xml; (item %d of %d)\n"; | |
79 | 91 | if( force_update || (access( dfile, F_OK ) != 0) ) |
80 | 92 | { |
81 | 93 | /* When performing an "update", or if no local copy is available... |
82 | 94 | * Force a "sync", to fetch a copy from the public host. |
83 | 95 | */ |
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 ); | |
85 | 115 | owner->SyncRepository( dname, repository ); |
86 | 116 | } |
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 ); | |
87 | 133 | |
88 | 134 | /* We SHOULD now have a locally cached copy of the package-list; |
89 | 135 | * attempt to merge it into the active profile database... |
@@ -94,8 +140,6 @@ void pkgRepository::GetPackageList( const char *dname ) | ||
94 | 140 | /* We successfully loaded the XML catalogue; refer to its |
95 | 141 | * root element... |
96 | 142 | */ |
97 | - if( pkgOptions()->Test( OPTION_VERBOSE ) > 1 ) | |
98 | - dmh_printf( "Load catalogue: %s.xml\n", dname ); | |
99 | 143 | pkgXmlNode *catalogue, *pkglist; |
100 | 144 | if( (catalogue = merge.GetRoot()) != NULL ) |
101 | 145 | { |
@@ -118,7 +162,31 @@ void pkgRepository::GetPackageList( const char *dname ) | ||
118 | 162 | /* Recursively incorporate any additional package lists, |
119 | 163 | * which may be specified within the current catalogue... |
120 | 164 | */ |
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 ); | |
122 | 190 | } |
123 | 191 | } |
124 | 192 | else |
@@ -135,6 +203,10 @@ void pkgRepository::GetPackageList( const char *dname ) | ||
135 | 203 | free( (void *)(dfile) ); |
136 | 204 | } |
137 | 205 | } |
206 | + /* Ensure that any accumulated diagnostics, pertaining to catalogue | |
207 | + * processing, have been displayed before wrapping up. | |
208 | + */ | |
209 | + dmh_control( DMH_END_DIGEST ); | |
138 | 210 | } |
139 | 211 | |
140 | 212 | void pkgRepository::GetPackageList( pkgXmlNode *catalogue ) |
@@ -182,6 +254,7 @@ pkgXmlNode *pkgXmlDocument::BindRepositories( bool force_update ) | ||
182 | 254 | /* Sanity check passed... |
183 | 255 | * Walk the XML data tree, selecting "repository" specifications... |
184 | 256 | */ |
257 | + pkgRepository::Reset(); | |
185 | 258 | pkgXmlNode *repository = dbase->FindFirstAssociate( repository_key ); |
186 | 259 | while( repository != NULL ) |
187 | 260 | { |
@@ -190,18 +263,25 @@ pkgXmlNode *pkgXmlDocument::BindRepositories( bool force_update ) | ||
190 | 263 | pkgRepository client( this, dbase, repository, force_update ); |
191 | 264 | pkgXmlNode *catalogue = repository->FindFirstAssociate( package_list_key ); |
192 | 265 | if( catalogue == NULL ) |
193 | - /* | |
194 | - * This repository specification doesn't identify any named | |
266 | + { | |
267 | + /* This repository specification doesn't identify any named | |
195 | 268 | * package list, so try the default, (which is named to match |
196 | 269 | * the XML key name for the "package-list" element)... |
197 | 270 | */ |
271 | + pkgRepository::IncrementTotal(); | |
198 | 272 | client.GetPackageList( package_list_key ); |
199 | - | |
273 | + } | |
200 | 274 | else |
201 | - /* At least one package list catalogue is specified; load it, | |
275 | + { /* At least one package list catalogue is specified; load it, | |
202 | 276 | * and any others which are explicitly identified... |
203 | 277 | */ |
278 | + pkgXmlNode *ref = catalogue; | |
279 | + do { pkgRepository::IncrementTotal(); | |
280 | + ref = ref->FindNextAssociate( package_list_key ); | |
281 | + } while( ref != NULL ); | |
282 | + | |
204 | 283 | client.GetPackageList( catalogue ); |
284 | + } | |
205 | 285 | |
206 | 286 | /* Similarly, a complete distribution may draw from an arbitrary set |
207 | 287 | * of distinct repositories; move on, to process the next repository |
@@ -26,12 +26,12 @@ | ||
26 | 26 | * |
27 | 27 | */ |
28 | 28 | #include "guimain.h" |
29 | +#include "pkgbase.h" | |
29 | 30 | #include "dmh.h" |
30 | 31 | |
31 | -using WTK::GenericDialogue; | |
32 | +using WTK::StringResource; | |
32 | 33 | using WTK::HorizontalSashWindowMaker; |
33 | 34 | using WTK::VerticalSashWindowMaker; |
34 | -using WTK::StringResource; | |
35 | 35 | |
36 | 36 | /* The main application window is divided into two |
37 | 37 | * horizontally adjustable sash panes; the following |
@@ -104,34 +104,6 @@ long AppWindowMaker::OnCreate() | ||
104 | 104 | return EXIT_SUCCESS; |
105 | 105 | } |
106 | 106 | |
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 | - | |
135 | 107 | #if 0 |
136 | 108 | /* FIXME: this stub implementation has been superseded by an |
137 | 109 | * alternative implementation in pkgdata.cpp; eventually, this |