6 * Written by Keith Marshall <keith@users.osdn.me>
7 * Copyright (C) 2012, 2013, 2020, MinGW.org Project
10 * Implementation of XML data loading services for the mingw-get GUI.
13 * This is free software. Permission is granted to copy, modify and
14 * redistribute this software, under the provisions of the GNU General
15 * Public License, Version 3, (or, at your option, any later version),
16 * as published by the Free Software Foundation; see the file COPYING
17 * for licensing details.
19 * Note, in particular, that this software is provided "as is", in the
20 * hope that it may prove useful, but WITHOUT WARRANTY OF ANY KIND; not
21 * even an implied WARRANTY OF MERCHANTABILITY, nor of FITNESS FOR ANY
22 * PARTICULAR PURPOSE. Under no circumstances will the author, or the
23 * MinGW Project, accept liability for any damages, however caused,
24 * arising from the use of this software.
36 #include <wtkexcept.h>
39 /* The DMH sub-system provides the following function, but it does not
40 * declare the prototype; (this is to obviate any automatic requirement
41 * for every DMH client to include windows.h). We have already included
42 * windows.h indirectly, via guimain.h, so it is convenient for us to
43 * declare the requisite prototype here.
45 EXTERN_C void dmh_setpty( HWND );
47 #define PROGRESS_METER_CLASS ProgressMeterMaker
49 class PROGRESS_METER_CLASS: public pkgProgressMeter
51 /* A locally defined class, supporting progress metering
52 * for package catalogue update and load operations.
55 PROGRESS_METER_CLASS( HWND, AppWindowMaker * );
56 ~PROGRESS_METER_CLASS(){ referrer->DetachProgressMeter( this ); }
58 virtual int Annotate( const char *, ... );
59 virtual void SetRange( int, int );
60 virtual void SetValue( int );
63 AppWindowMaker *referrer;
64 HWND annotation, count, lim, frac, progress_bar;
65 void PutVal( HWND, const char *, ... );
70 pkgProgressMeter *AppWindowMaker::AttachProgressMeter( pkgProgressMeter *meter )
72 /* A local helper method for attaching a progress meter to the
73 * controlling class instance for the main application window.
75 if( AttachedProgressMeter == NULL )
76 AttachedProgressMeter = meter;
77 return AttachedProgressMeter;
80 inline void AppWindowMaker::DetachProgressMeter( pkgProgressMeter *meter )
82 /* A local helper method for detaching a progress meter from the
83 * controlling class instance for the main application window.
85 if( meter == AttachedProgressMeter )
87 pkgData->DetachProgressMeter( meter );
88 AttachedProgressMeter = NULL;
92 /* We must also provide the implementation of our local progress meter class.
94 #define IDD( DLG, ITEM ) GetDlgItem( DLG, IDD_PROGRESS_##ITEM )
95 PROGRESS_METER_CLASS::PROGRESS_METER_CLASS( HWND dlg, AppWindowMaker *owner ):
96 referrer( owner ), annotation( IDD( dlg, MSG ) ), progress_bar( IDD( dlg, BAR ) ),
97 count( IDD( dlg, VAL ) ), lim( IDD( dlg, MAX ) ), frac( IDD( dlg, PCT ) )
99 /* Constructor creates an instance of the progress meter class, attaching it
100 * to the main application window, with an initial metering range of 0..100%,
101 * and a starting indicated completion state of 0%.
103 owner->AttachProgressMeter( this );
104 SetRange( 0, 1 ); SetValue( 0 );
108 #include "pmihook.cpp"
110 /* Implementation of service routines, for loading the package catalogue
111 * from its defining collection of XML files.
113 void AppWindowMaker::LoadPackageData( bool force_update )
115 /* Helper method to load the package database from its
116 * defining collection of XML catalogue files.
119 if( pkgData == NULL )
121 /* This is the first request to load the database;
122 * establish the load starting point as "profile.xml",
125 if( access( dfile = xmlfile( profile_key ), R_OK ) != 0 )
127 /* ...or as "defaults.xml" otherwise.
129 free( (void *)(dfile) );
130 dfile = xmlfile( defaults_key );
134 { /* This is a reload request; in this case we adopt the
135 * starting point as established for the initial load...
137 dfile = strdup( pkgData->Value() );
139 * ...and clear out all stale data from the previous
144 /* Commence loading...
146 pkgData = new pkgXmlDocument( dfile );
147 if( (pkgData == NULL) || ! pkgData->IsOk() )
149 * ...bailing out on failure to access the initial file.
151 throw WTK::runtime_error( WTK::error_text(
152 "%s: cannot open package database", dfile
155 /* Once the initial file has been loaded, its name is
156 * recorded within the XML data image itself; thus, we
157 * may release the heap memory used to establish it
158 * prior to opening the file.
160 free( (void *)(dfile) );
162 /* Create a graft point for attachment of the package
163 * group hierarchy tree to the loaded XML data image.
165 pkgInitCategoryTreeGraft( pkgData->GetRoot() );
167 /* Establish the repository URI references, for retrieval
168 * of the downloadable catalogue files, and load them...
170 pkgData->AttachProgressMeter( AttachedProgressMeter );
171 if( pkgData->BindRepositories( force_update ) == NULL )
173 * ...once again, bailing out on failure.
175 throw WTK::runtime_error( "Cannot read package catalogue" );
177 /* Finally, load the installation records pertaining to
178 * the active system map...
180 pkgData->LoadSystemMap();
182 /* ...and establish any preferences which the user may have
183 * specified within profile.xml
185 pkgData->EstablishPreferences( "gui" );
188 static void pkgInvokeInitDataLoad( void *window )
190 /* Thread procedure for performing the initial catalogue load, on
191 * application start-up. This will load from locally cached data
192 * files, when available; however, it will also initiate a download
193 * from the remote repository, for any file which is missing from
194 * the local cache. Since this may be a time consuming process,
195 * we subject it to progress metering, to ensure that the user is
196 * not left staring at an apparently hung, blank window.
198 AppWindowMaker *app = GetAppWindow( GetParent( (HWND)(window) ));
199 SendMessage( (HWND)(window),
200 WM_SETTEXT, 0, (LPARAM)("Loading Package Catalogue")
202 ProgressMeterMaker ui( (HWND)(window), app );
204 /* For this activity, we request automatic dismissal of the dialogue,
205 * when loading has been completed; the user will have an opportunity
206 * to countermand this choice, if loading is delayed by the required
207 * download of any missing local catalogue file.
209 HWND dlg = GetDlgItem( (HWND)(window), IDD_AUTO_CLOSE_OPTION );
210 SendMessage( dlg, WM_SETTEXT, 0,
211 (LPARAM)("Close dialogue automatically, when loading is complete.")
213 CheckDlgButton( (HWND)(window), IDD_AUTO_CLOSE_OPTION, BST_CHECKED );
215 /* We've now set up the initial state for the progress meter dialogue;
216 * proceed to load, (and perhaps download), the XML data files.
218 app->LoadPackageData( false );
220 /* When loading has been completed, automatically dismiss the dialogue...
222 if( IsDlgButtonChecked( (HWND)(window), IDD_AUTO_CLOSE_OPTION ) )
223 SendMessage( (HWND)(window), WM_COMMAND, (WPARAM)(IDOK), 0 );
225 /* ...unless the user has countermanded the automatic dismissal request...
228 { /* ...in which case, we activate the manual dismissal button...
230 if( (dlg = GetDlgItem( (HWND)(window), IDOK )) != NULL )
231 EnableWindow( dlg, TRUE );
233 /* ...and notify the user that it must be clicked to continue.
235 ui.Annotate( "Data has been loaded; please close this dialogue to continue." );
239 static void pkgInvokeUpdate( void *window )
241 /* Thread procedure for performing a package catalogue update.
242 * This will download catalogue files from the remote repository,
243 * and integrate them into the locally cached catalogue XML file
244 * set. Since this is normally a time consuming process, we must
245 * subject it to progress metering, to ensure that the user is
246 * not left staring at an apparently hung, blank window.
248 AppWindowMaker *app = GetAppWindow( GetParent( (HWND)(window) ));
249 ProgressMeterMaker ui( (HWND)(window), app );
251 /* After setting up the progress meter, we clear out any data
252 * which was previously loaded into the package list, reload it
253 * with the "forced download" option, and refresh the display.
255 app->ClearPackageList();
256 app->LoadPackageData( true );
257 app->UpdatePackageList();
259 /* During the update, the user may have selected the option for
260 * automatic dismissal of the dialogue box on completion...
262 if( IsDlgButtonChecked( (HWND)(window), IDD_AUTO_CLOSE_OPTION ) )
264 * ...in which case, we dismiss it without further ado...
266 SendMessage( (HWND)(window), WM_COMMAND, (WPARAM)(IDOK), 0 );
269 { /* ...otherwise, we activate the manual dismissal button...
271 HWND dlg = GetDlgItem( (HWND)(window), IDOK );
272 if( dlg != NULL ) EnableWindow( dlg, TRUE );
274 /* ...and notify the user that it must be clicked to continue.
276 ui.Annotate( "Update is complete; please close this dialogue to continue." );
280 inline void AppWindowMaker::ExecuteScheduledActions( void )
282 /* Helper method to delegate execution of a schedule of actions from
283 * the application window to its associated action item controller.
285 pkgData->Schedule()->Execute( false );
286 pkgData->UpdateSystemMap();
289 class pkgDialogueSpinWait: public pkgSpinWait
291 /* Derivative class for redirection of pkgSpinWait::Report()
292 * messages to a specified dialogue box text control.
295 pkgDialogueSpinWait( HWND msg ): dlg( msg ){}
298 virtual int DispatchReport( const char *, va_list );
302 int pkgDialogueSpinWait::DispatchReport( const char *fmt, va_list argv )
304 /* Method to handle pkgSpinWait::Report() message redirection.
306 char buf[ 1 + vsnprintf( NULL, 0, fmt, argv ) ];
307 int count = vsnprintf( buf, sizeof( buf ), fmt, argv );
308 SendMessage( dlg, WM_SETTEXT, 0, (LPARAM)(buf) );
312 static void pkgApplyChanges( void *window )
314 /* Worker thread processing function, run while displaying the
315 * IDD_APPLY_EXECUTE dialogue box, to apply scheduled changes.
317 HWND msg = GetDlgItem( (HWND)(window), IDD_PROGRESS_MSG );
318 AppWindowMaker *app = GetAppWindow( GetParent( (HWND)(window) ) );
320 /* Set up progess reporting and diagnostic message display
321 * channels, and execute the scheduled actions.
323 pkgDialogueSpinWait stat( msg );
324 dmh_setpty( GetDlgItem( (HWND)(window), IDD_DMH_CONSOLE ) );
325 app->ExecuteScheduledActions();
328 /* Check for successful application of all scheduled changes.
330 int error_count = app->EnumerateActions( ACTION_UNSUCCESSFUL );
332 /* During processing, the user may have selected the option for
333 * automatic dismissal of the dialogue box on completion...
335 if( (error_count == 0)
336 && IsDlgButtonChecked( (HWND)(window), IDD_AUTO_CLOSE_OPTION ) )
338 * ...in which case, and provided all changes were applied
339 * successfully, we dismiss it without further ado...
341 SendMessage( (HWND)(window), WM_COMMAND, (WPARAM)(IDOK), 0 );
344 { /* ...otherwise, we activate the manual dismissal button...
347 if( (dlg = GetDlgItem( (HWND)(window), IDOK )) != NULL )
348 EnableWindow( dlg, TRUE );
350 /* ...and notify the user that it must be clicked to continue.
352 stat.Report( (error_count == 0)
353 ? "All changes were applied successfully;"
354 " you may now close this dialogue."
355 : "Not all changes were applied successfully;"
356 " please refer to details below."
361 static int CALLBACK pkgDialogue
362 ( HWND window, unsigned int msg, WPARAM wParam, LPARAM lParam )
364 /* Generic handler for dialogue boxes which delegate an associated
365 * processing activity to a background thread.
369 /* We need to handle only two classes of windows messages
370 * on behalf of such dialogue boxes...
373 /* ...viz. on initial dialogue box creation, we delegate the
374 * designated activity to the background thread...
376 _beginthread( AppWindowMaker::DialogueThread, 0, (void *)(window) );
380 if( LOWORD( wParam ) == IDOK )
382 /* ...then we wait for a notification that the dialogue may be
383 * closed, (which isn't permitted until the thread completes).
385 EndDialog( window, 0 );
389 /* Any other messages, which are directed to this dialogue box,
390 * may be safely ignored.
395 /* The following static member of the AppWindowMaker class is used
396 * to pass the function reference for the worker thread process to
397 * the preceding dialogue box handler function, when it is invoked
398 * by the following helper method.
400 pkgDialogueThread *AppWindowMaker::DialogueThread = NULL;
402 int AppWindowMaker::DispatchDialogueThread( int id, pkgDialogueThread *handler )
404 /* Helper method to open a dialogue box, and to initiate a worker
405 * thread to handle a designated background process on its behalf.
407 DialogueThread = handler;
408 return DialogueResponse( id, pkgDialogue );
411 inline unsigned long AppWindowMaker::EnumerateActions( int classified )
413 /* Helper method to enumerate the actions of a specified
414 * class, within the current schedule.
416 return pkgData->Schedule()->EnumeratePendingActions( classified );
419 inline pkgActionItem *pkgActionItem::SuppressRedundantUpgrades( void )
421 /* Helper method to adjust the schedule of upgrades, after marking
422 * all installed packages, to exclude all those which are already at
423 * the most recently available release.
426 if( (head = this) != NULL )
428 /* First, provided the schedule is not empty, we walk the list
429 * of scheduled actions, until we find the true first entry...
431 while( head->prev != NULL ) head = head->prev;
432 for( pkgActionItem *ref = head; ref != NULL; ref = ref->next )
434 /* ...and then, we process the list from first entry to last,
435 * selecting those entries which schedule an upgrade action...
437 if( ((ref->flags & ACTION_MASK) == ACTION_UPGRADE)
439 * ...and for which the currently installed release is the
440 * same as that which an upgrade would install...
442 && (ref->selection[ to_install ] == ref->selection[ to_remove ]) )
444 * ...in which case, we mark this entry for "no action".
446 ref->flags &= ~ACTION_MASK;
449 /* Finally, we return a pointer to the first entry in the schedule.
454 static int pkgActionCount( HWND dlg, int id, const char *fmt, int classified )
456 /* Helper function to itemise the currently scheduled actions
457 * of a specified class, recording the associated package name
458 * within the passed EDITTEXT control, and noting the count of
459 * itemised actions within the heading of the control.
461 * First, count the actions, while adding the package names
462 * to the edit control with the specified ID.
464 dmh_setpty( GetDlgItem( dlg, id ) );
465 int count = GetAppWindow( GetParent( dlg ))->EnumerateActions( classified );
467 /* Construct the heading, according to the specified format,
468 * and including the identified action count.
470 const char *packages = (count == 1) ? "package" : "packages";
471 char label_text[1 + snprintf( NULL, 0, fmt, count, packages )];
472 snprintf( label_text, sizeof( label_text), fmt, count, packages );
474 /* Finally, update the heading on the edit control, assigning
475 * it to the dialogue control with ID one greater than that of
476 * the edit control itself.
478 SendMessage( GetDlgItem( dlg, ++id ), WM_SETTEXT, 0, (LPARAM)(label_text) );
483 /* Implement a macro as shorthand notation for passing of action
484 * specific argument lists to the pkgActionCount() function...
486 #define ACTION_APPLY(OP) APPLIES_TO(OP), FMT_APPLY_##OP##S, ACTION_##OP
487 #define APPLIES_TO(OP) IDD_APPLY_##OP##S_PACKAGES
489 /* ...using the appropriate selection from these action specific
492 #define FMT_APPLY_REMOVES "%u installed %s will be removed"
493 #define FMT_APPLY_UPGRADES "%u installed %s will be upgraded"
494 #define FMT_APPLY_INSTALLS "%u new/upgraded %s will be installed"
496 static int CALLBACK pkgApplyApproved
497 ( HWND window, unsigned int msg, WPARAM wParam, LPARAM lParam )
499 /* Callback function servicing the custom dialogue box in which
500 * scheduled actions are itemised for user confirmation, prior
504 { case WM_INITDIALOG:
505 /* On opening the dialogue box, itemise each of the
506 * remove, upgrade, and install action classes.
508 pkgActionCount( window, ACTION_APPLY( REMOVE ) );
509 pkgActionCount( window, ACTION_APPLY( UPGRADE ) );
510 pkgActionCount( window, ACTION_APPLY( INSTALL ) );
514 /* Wait for the user to review the itemised schedule
515 * of pending changes, confirm intent to proceed...
517 long opt = LOWORD( wParam );
518 if( (opt == ID_APPLY) || (opt == ID_DISCARD) || (opt == ID_DEFER) )
520 /* ...then close the dialogue, passing the selected
521 * continuation option back to the caller.
523 EndDialog( window, opt );
527 /* Ignore any window messages we don't recognise.
532 int AppWindowMaker::Invoked( void )
534 /* Override for the WTK::MainWindowMaker::Invoked() method; it
535 * provides the hook for the initial loading of the XML database,
536 * and creation of the display controls through which its content
537 * will be presented to the user, prior to invocation of the main
538 * window's message loop.
540 * The data displays depend on the MS-Windows Common Controls API;
541 * initialise all components of this up front.
543 InitCommonControls();
545 /* Load the data from the XML catalogue files; this activity
546 * is invoked in a background thread, initiated from a progress
547 * dialogue derived from the "Update Catalogue" template.
549 DispatchDialogueThread( IDD_REPO_UPDATE, pkgInvokeInitDataLoad );
551 /* Establish the initial views of the package category selection
552 * tree, and the list of available packages; (the initial package
553 * list includes everything in the "All Packages" category).
555 InitPackageTreeView();
556 InitPackageListView();
558 /* Initialise the data-sheet tab control, displaying the default
559 * "no package selected" message.
561 InitPackageTabControl();
563 /* Force a layout adjustment, to ensure that the displayed
564 * data controls are correctly populated.
567 InvalidateRect( AppWindow, NULL, FALSE );
568 UpdateWindow( AppWindow );
570 /* Finally, we may delegate all further processing to the main
571 * window's message loop.
573 return WTK::MainWindowMaker::Invoked();
576 /* The following static entities are provided to facilitate the
577 * implementation of the "discard changes" confirmation dialogue.
579 static const char *pkgConfirmAction = NULL;
580 static AppWindowMaker *pkgConfirmActionClient = NULL;
582 static void dlgReplaceText( HWND dlg, ... )
584 /* Local helper routine, used by AppWindowMaker::ConfirmActionDialogue(),
585 * to substitute the designated action description into the format strings,
586 * as specified within the dialogue box template.
588 * First step is to copy the format specification from the dialogue item.
590 int len = 1 + SendMessage( dlg, WM_GETTEXTLENGTH, 0, 0 );
591 char fmt[len]; SendMessage( dlg, WM_GETTEXT, len, (LPARAM)(fmt) );
593 /* Having established the format, we allocate a sufficient buffer, in
594 * which we prepare the formatted text...
596 va_list argv; va_start( argv, dlg );
597 char text[1 + vsnprintf( NULL, 0, fmt, argv )];
598 vsnprintf( text, sizeof( text ), fmt, argv );
601 /* ...which we then copy back to the active dialogue item.
603 SendMessage( dlg, WM_SETTEXT, 0, (LPARAM)(text) );
606 int CALLBACK AppWindowMaker::ConfirmActionDialogue
607 ( HWND dlg, unsigned int msg, WPARAM wparam, LPARAM lparam )
608 #define DIALOGUE_CHKSTATE pkgConfirmActionClient->EnumerateActions
609 #define DIALOGUE_DISPATCH pkgConfirmActionClient->DispatchDialogueThread
610 #define DIALOGUE_RESPONSE pkgConfirmActionClient->DialogueResponse
612 /* Call back handler for the messages generated by the "discard changes"
613 * confirmation dialogue.
616 { case WM_INITDIALOG:
617 /* When the confirmation dialogue is invoked, we centre it within
618 * its owner window, and replace the format specification strings
619 * from its text controls with their formatted equivalents.
621 WTK::AlignWindow( dlg, WTK_ALIGN_CENTRED );
622 dlgReplaceText( GetDlgItem( dlg, IDD_PROGRESS_MSG ), pkgConfirmAction );
623 dlgReplaceText( GetDlgItem( dlg, IDD_PROGRESS_TXT ), pkgConfirmAction,
629 /* Process the messages from the dialogue's three command buttons:
631 switch( msg = LOWORD( wparam ) )
634 /* Sent when the user selects the "Discard Changes" option,
635 * we return "IDOK" to confirm that the action should be
636 * progressed, ignoring any uncommitted changes.
638 EndDialog( dlg, IDOK );
642 /* Sent when the user selects the "Review Changes" option; we
643 * shelve the active dialogue, and delegate the decision as to
644 * whether to proceed to the "Apply Changes" dialogue.
646 ShowWindow( dlg, SW_HIDE );
647 switch( DIALOGUE_RESPONSE( IDD_APPLY_APPROVE, pkgApplyApproved ) )
650 /* Handle a "Defer" request from the "Apply Changes"
651 * dialogue as equivalent to our own "Cancel Request"
658 /* When "Apply" confirmation is forthcoming, we proceed to
659 * download any required packages, and invoke the scheduled
660 * remove, upgrade, or install actions.
662 DIALOGUE_DISPATCH( IDD_APPLY_DOWNLOAD, pkgInvokeDownload );
663 DIALOGUE_DISPATCH( IDD_APPLY_EXECUTE, pkgApplyChanges );
665 /* Ensure that all "Apply" actions completed successfully...
667 if( DIALOGUE_CHKSTATE( ACTION_UNSUCCESSFUL ) > 0 )
669 * ...otherwise, cancel the ensuing action, so that the
670 * user may have an opportunity to remedy the problem.
674 /* Regardless of the outcome from the "Review Changes" activity,
675 * simply fall through to terminate the dialogue appropriately.
678 /* Sent when the user selects the "Cancel Request" option; we
679 * simply allow the message ID to propagate back to the caller,
680 * as the response code.
682 EndDialog( dlg, msg );
686 /* We don't handle any other message; if any is received, mark it as
687 * "unhandled", and pass it on to the default dialogue handler.
692 bool AppWindowMaker::ConfirmActionRequest( const char *desc )
694 /* Helper to confirm user's intent to proceed, when a request
695 * to update the catalogue, or to quit, would result in pending
696 * installation actions being discarded.
698 * We begin by saving reference data to be passed to the static
699 * dialogue handler method...
701 pkgConfirmAction = desc;
702 pkgConfirmActionClient = this;
704 /* ...prior to checking for pending actions, and invoking the
705 * dialogue procedure when appropriate, or simply allowing the
706 * action to proceed, otherwise.
708 pkgActionItem *pending = pkgData->Schedule();
709 return (pending && pending->EnumeratePendingActions() > 0)
710 ? DialogBox( AppInstance, MAKEINTRESOURCE( IDD_CONFIRMATION ),
711 AppWindow, ConfirmActionDialogue
716 long AppWindowMaker::OnCommand( WPARAM cmd )
717 #define ACTION_PRESERVE_FAILED (ACTION_DOWNLOAD_FAILED | ACTION_APPLY_FAILED)
719 /* Handler for WM_COMMAND messages which are directed to the
720 * top level application window.
723 { case IDM_HELP_ABOUT:
724 /* This request is initiated by selecting "About ...", ...
726 case IDM_HELP_LEGEND:
727 /* ...and this one by selecting "Icon Legend", from the "Help"
728 * menu; in both cases, we respond by displaying an associated
731 WTK::GenericDialogue( AppInstance, AppWindow, cmd );
734 case IDM_PACKAGE_INSTALL:
735 /* Initiated by selecting the "Mark for Installation" option
736 * from the "Package" menu, this request will schedule the
737 * currently selected package, and any currently unfulfilled
738 * dependencies, for installation.
740 Schedule( ACTION_INSTALL );
743 case IDM_PACKAGE_UPGRADE:
744 /* Initiated by selecting the "Mark for Upgrade" option
745 * from the "Package" menu, this request will schedule the
746 * currently selected package, and any currently unfulfilled
747 * dependencies, for upgrade or installation, as appropriate.
749 Schedule( ACTION_UPGRADE );
752 case IDM_PACKAGE_REMOVE:
753 /* Initiated by selecting the "Mark for Removal" option
754 * from the "Package" menu, this request will schedule the
755 * currently selected package for removal.
757 Schedule( ACTION_REMOVE );
760 case IDM_PACKAGE_UNMARK:
761 /* Initiated by selecting the "Unmark" option from the
762 * "Package" menu, this request will cancel the effect of
763 * any previously scheduled action, in respect of the
764 * currently selected package.
766 UnmarkSelectedPackage();
769 case IDM_REPO_UPDATE:
770 /* This request is initiated by selecting "Update Catalogue"
771 * from the "Repository" menu; we respond by initiating a progress
772 * dialogue, from which a background thread is invoked to download
773 * fresh copies of the package catalogue files from the remote
774 * repository, and consolidate them into the local catalogue.
776 * Note that this activity will cause any pending installation
777 * actions to be discarded; seek confirmation of user's intent
778 * to proceed, when this situation prevails.
780 if( ConfirmActionRequest( "update the catalogue" ) )
781 { DispatchDialogueThread( IDD_REPO_UPDATE, pkgInvokeUpdate );
782 UpdatePackageMenuBindings();
786 case IDM_REPO_MARK_UPGRADES:
787 /* Initiated when the user selects the "Mark All Upgrades"
788 * option; in this case, we identify all packages which are
789 * already installed, and for which upgrades are available,
790 * and schedule an upgrade action in respect of each.
792 pkgData->RescheduleInstalledPackages( ACTION_UPGRADE );
794 /* After scheduling all available upgrades, we must
795 * update the package list view marker icons...
797 pkgListViewMaker pkglist( PackageListView );
798 pkglist.MarkScheduledActions(
799 pkgData->Schedule()->SuppressRedundantUpgrades()
802 /* ...and also adjust the menu bindings accordingly.
804 UpdatePackageMenuBindings();
808 /* Initiated when the user selects the "Apply Changes" option,
809 * we first reset the error trapping and download request state
810 * for all scheduled actions, then we present the user with a
811 * dialogue requesting confirmation of approval to proceed.
813 pkgData->Schedule()->Assert( 0UL, ~ACTION_PRESERVE_FAILED );
814 switch( DialogueResponse( IDD_APPLY_APPROVE, pkgApplyApproved ) )
816 /* Of the three possible responses, we simply ignore the "Defer"
817 * option, (since it requires no action), but we must explicitly
818 * handle the "Apply" and "Discard" options.
821 /* When "Apply" confirmation is forthcoming, we proceed to
822 * download any required packages, and invoke the scheduled
823 * remove, upgrade, or install actions.
825 DispatchDialogueThread( IDD_APPLY_DOWNLOAD, pkgInvokeDownload );
826 DispatchDialogueThread( IDD_APPLY_EXECUTE, pkgApplyChanges );
828 /* After applying changes, we fall through...
831 /* ...so that on explicit "Discard" selection by the user,
832 * or following application of all scheduled actions, as a
833 * result of processing the "Apply" selection, we clear the
834 * actions schedule, remove all marker icons, and refresh
835 * the package list to reflect current status.
837 pkgListViewMaker pkglist( PackageListView );
838 pkglist.UpdateListView();
840 /* Updating the list view clears pending action marks from
841 * every entry, but clearing the schedule may not cancel any
842 * request relating to a failed action; restore marked state
843 * for such residual actions.
845 pkglist.MarkScheduledActions(
846 pkgData->ClearScheduledActions( ACTION_PRESERVE_FAILED )
849 /* Clearing the schedule of actions may also affect the
850 * validity of menu options; update accordingly.
852 UpdatePackageMenuBindings();
857 /* This request is initiated by selecting the "Quit" option
858 * from the "Repository" menu; we respond by sending a WM_CLOSE
859 * message, to terminate the current application instance.
861 SendMessage( AppWindow, WM_CLOSE, 0, 0L );
864 /* Any other message is silently ignored.
869 /* $RCSfile$: end of file */