6 * Written by Keith Marshall <keithmarshall@users.sourceforge.net>
7 * Copyright (C) 2012, 2013, MinGW.org Project
10 * Implementation of the network download agent interface, through
11 * which the mingw-get GUI requests the services of the mingw-get DLL
12 * to get (download) package archives from internet repositories.
15 * This is free software. Permission is granted to copy, modify and
16 * redistribute this software, under the provisions of the GNU General
17 * Public License, Version 3, (or, at your option, any later version),
18 * as published by the Free Software Foundation; see the file COPYING
19 * for licensing details.
21 * Note, in particular, that this software is provided "as is", in the
22 * hope that it may prove useful, but WITHOUT WARRANTY OF ANY KIND; not
23 * even an implied WARRANTY OF MERCHANTABILITY, nor of FITNESS FOR ANY
24 * PARTICULAR PURPOSE. Under no circumstances will the author, or the
25 * MinGW Project, accept liability for any damages, however caused,
26 * arising from the use of this software.
35 class pkgDownloadMeterGUI: public pkgDownloadMeter
37 /* A locally defined class, through which the download agent
38 * may transmit progress monitoring data to the designated GUI
39 * download monitoring dialogue box.
42 pkgDownloadMeterGUI( HWND );
44 virtual void ResetGUI( const char *, unsigned long );
45 virtual int Update( unsigned long );
47 inline ~pkgDownloadMeterGUI();
50 static int spin_index, spin_active;
51 HWND file_name, file_size, copy_size, copy_frac, progress_bar;
52 virtual void SpinWaitAction( int, const char * );
53 static const char *host; static HWND status_hook;
54 static void SpinWait( void * );
57 /* pkgDownloadMeterGUI needs a prototype for the dmh_setpty() function;
58 * this isn't declared in any DMH specific header, since it requires a
59 * declaration of HWND, but DMH prefers to avoid namespace pollution by
60 * such windows specific type definitions, so declare it here.
62 EXTERN_C void dmh_setpty( HWND );
66 pkgDownloadMeterGUI::pkgDownloadMeterGUI( HWND dialogue )
68 /* Establish the global reference, through which the download
69 * agent will obtain access to the GUI dialogue...
73 /* ...and store references to its data controls.
75 file_name = GetDlgItem( dialogue, IDD_PROGRESS_MSG );
76 file_size = GetDlgItem( dialogue, IDD_PROGRESS_MAX );
77 copy_size = GetDlgItem( dialogue, IDD_PROGRESS_VAL );
78 copy_frac = GetDlgItem( dialogue, IDD_PROGRESS_PCT );
79 progress_bar = GetDlgItem( dialogue, IDD_PROGRESS_BAR );
81 /* Attach a pseudo-terminal emulating window, for capture
82 * of download specific diagnostic messages.
84 dmh_setpty( GetDlgItem( dialogue, IDD_DMH_CONSOLE ) );
87 /* Initialise static variables, used to maintain status for
88 * the SpinWait() method.
90 int pkgDownloadMeterGUI::spin_index = 0;
91 int pkgDownloadMeterGUI::spin_active = 0;
93 /* Likewise, its static reference pointers.
95 const char *pkgDownloadMeterGUI::host = NULL;
96 HWND pkgDownloadMeterGUI::status_hook = NULL;
98 void pkgDownloadMeterGUI::SpinWait( void * )
100 /* Static method; this provides the thread procedure,
101 * through which the spin wait status notification is
102 * written to the dialogue box.
104 static const char *marker = "|/-\\";
105 static const char *msg = "Connecting to %s ... %c";
107 /* Provide a local buffer, in which the spin wait status
108 * notification message may be formatted.
110 char status_text[1 + snprintf( NULL, 0, msg, host, marker[ spin_index ])];
112 /* Prepare and display the notification message...
114 spin_active = 1; spin_index = 0;
115 do { sprintf( status_text, msg, host, marker[ spin_index ] );
116 SendMessage( status_hook, WM_SETTEXT, 0, (LPARAM)(status_text) );
118 * ...then refresh it at one second intervals, using
119 * a different marker character for each cycle...
121 Sleep( 1000 ); spin_index = (1 + spin_index) % 4;
123 * ...until requested to terminate the thread.
125 } while( spin_active == 1 );
127 /* When done, release the buffer used to pass the download
128 * host domain identification, and mark the state as idle.
130 free( (void *)(host) ); host = NULL;
134 void pkgDownloadMeterGUI::SpinWaitAction( int run, const char *uri )
136 /* Dispatcher method, used to start and stop the preceding
141 /* This is a "stop" request; provided there is a spin-wait
142 * thread active, we signal it to stop, then wait for it to
143 * acknowledge that it has done so.
145 if( spin_active == 1 )
147 while( spin_active == 2 )
151 else if( spin_active == 0 )
153 /* This is a "start" request; before we dispatch it, we
154 * must identify the domain name for the download host, so
155 * that it may be displayed in the notification message.
157 for( int i = 5; i > 3; i-- )
159 * First, we identify any prefixed protocol designator...
161 if( strncasecmp( "https", uri, i ) == 0 ) { uri += i; i = 1; }
162 if( *uri == ':' ) while( *++uri == '/' )
164 * ...removing it, and all following field delimiter
165 * characters, from the start of the URI string...
168 /* Then, we allocate a local buffer, of sufficient size to
169 * accommodate the remainder of the URI...
171 char buf[1 + strlen( uri )]; char *p = buf;
173 /* ...into which we copy just the domain name fragment...
175 while( *uri && (*uri != '/') ) *p++ = *uri++;
177 /* ...before terminating it, and copying to heap memory.
179 *p = '\0'; host = strdup( buf );
181 /* Finally, we assign the file name to be displayed, when the
182 * connection is complete, and invoke the spin wait.
184 status_hook = file_name; _beginthread( SpinWait, 0, NULL );
188 void pkgDownloadMeterGUI::ResetGUI( const char *filename, unsigned long size )
190 /* Method to reset the displayed content of the dialogue box to
191 * an appropriate initial state, in preparation for monitoring the
192 * download of a new archive file; the name of this file, and its
193 * anticipated size are preset, as specified by the arguments.
195 char buf[12]; SizeFormat( buf, 0 ); spin_index = 0;
196 SendMessage( file_name, WM_SETTEXT, 0, (LPARAM)(filename) );
197 SendMessage( copy_size, WM_SETTEXT, 0, (LPARAM)(buf) );
198 SizeFormat( buf, content_length = size );
199 SendMessage( file_size, WM_SETTEXT, 0, (LPARAM)(buf) );
200 SendMessage( copy_frac, WM_SETTEXT, 0, (LPARAM)("0 %") );
201 SendMessage( progress_bar, PBM_SETRANGE, 0, MAKELPARAM( 0, 100 ) );
202 SendMessage( progress_bar, PBM_SETPOS, 0, 0 );
205 int pkgDownloadMeterGUI::Update( unsigned long count )
207 /* Method to update the download progress report; the download
208 * agent invokes this after each block of archive data has been
209 * received from the repository host, so that this method may
210 * refresh the progress counters in the dialogue box.
214 /* First, we update the display of the actual byte count...
216 SizeFormat( buf, count );
217 SendMessage( copy_size, WM_SETTEXT, 0, (LPARAM)(buf) );
219 /* ...then we convert that byte count to a percentage of
220 * the anticipated file size, using the result to update
221 * the percentage completed, and the progress bar.
223 count = (count * 100) / content_length;
224 if( snprintf( buf, sizeof( buf ), "%d %%", count ) >= sizeof( buf ) )
225 strcpy( buf, "*** %" );
226 SendMessage( copy_frac, WM_SETTEXT, 0, (LPARAM)(buf) );
227 SendMessage( progress_bar, PBM_SETPOS, count, 0 );
232 inline pkgDownloadMeterGUI::~pkgDownloadMeterGUI()
234 /* This must close the pseudo-terminal attachment, if any, which
235 * was established for use by the diagnostic message handler...
239 /* ...and reset the global reference pointer, so the download
240 * agent will not attempt to access a dialogue box which has been
241 * closed by the GUI application.
246 inline void pkgActionItem::DownloadArchiveFiles( void )
248 /* Helper method, invoked by the GUI application, to initiate
249 * an in-order traversal of the schedule of actions...
251 pkgActionItem *current;
252 if( (current = this) != NULL )
254 /* ...ensuring that the traversal commences at the first of
255 * the scheduled actions...
257 while( current->prev != NULL ) current = current->prev;
259 /* ...to download all requisite archive files.
261 DownloadArchiveFiles( current );
265 #if IMPLEMENTATION_LEVEL == PACKAGE_BASE_COMPONENT
267 inline void AppWindowMaker::DownloadArchiveFiles( void )
269 /* Helper method to redirect a request to initiate package
270 * download, from the controlling application window object
271 * to its associated schedule of actions object.
273 pkgData->Schedule()->DownloadArchiveFiles();
276 EXTERN_C void pkgInvokeDownload( void *window )
278 /* Worker thread procedure, invoked by the OnCommand() method
279 * of the application window object, when initiating the archive
280 * download process via the appropriate monitoring dialogue.
282 pkgDownloadMeterGUI metered( (HWND)(window) );
283 GetAppWindow( GetParent( (HWND)(window) ))->DownloadArchiveFiles();
284 SendMessage( (HWND)(window), WM_COMMAND, (WPARAM)(IDOK), 0 );
289 /* $RCSfile$: end of file */