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 );
107 #include "pmihook.cpp"
109 /* Implementation of service routines, for loading the package catalogue
110 * from its defining collection of XML files.
112 void AppWindowMaker::LoadPackageData( bool force_update )
114 /* Helper method to load the package database from its
115 * defining collection of XML catalogue files.
118 if( pkgData == NULL )
120 /* This is the first request to load the database;
121 * establish the load starting point as "profile.xml",
124 if( access( dfile = xmlfile( profile_key ), R_OK ) != 0 )
126 /* ...or as "defaults.xml" otherwise.
128 free( (void *)(dfile) );
129 dfile = xmlfile( defaults_key );
133 { /* This is a reload request; in this case we adopt the
134 * starting point as established for the initial load...
136 dfile = strdup( pkgData->Value() );
138 * ...and clear out all stale data from the previous
143 /* Commence loading...
145 pkgData = new pkgXmlDocument( dfile );
146 if( (pkgData == NULL) || ! pkgData->IsOk() )
148 * ...bailing out on failure to access the initial file.
150 throw WTK::runtime_error( WTK::error_text(
151 "%s: cannot open package database", dfile
154 /* Once the initial file has been loaded, its name is
155 * recorded within the XML data image itself; thus, we
156 * may release the heap memory used to establish it
157 * prior to opening the file.
159 free( (void *)(dfile) );
161 /* Create a graft point for attachment of the package
162 * group hierarchy tree to the loaded XML data image.
164 pkgInitCategoryTreeGraft( pkgData->GetRoot() );
166 /* Establish the repository URI references, for retrieval
167 * of the downloadable catalogue files, and load them...
169 pkgData->AttachProgressMeter( AttachedProgressMeter );
170 if( pkgData->BindRepositories( force_update ) == NULL )
172 * ...once again, bailing out on failure.
174 throw WTK::runtime_error( "Cannot read package catalogue" );
176 /* Finally, load the installation records pertaining to
177 * the active system map...
179 pkgData->LoadSystemMap();
181 /* ...and establish any preferences which the user may have
182 * specified within profile.xml
184 pkgData->EstablishPreferences( "gui" );
187 static void pkgInvokeInitDataLoad( void *window )
189 /* Thread procedure for performing the initial catalogue load, on
190 * application start-up. This will load from locally cached data
191 * files, when available; however, it will also initiate a download
192 * from the remote repository, for any file which is missing from
193 * the local cache. Since this may be a time consuming process,
194 * we subject it to progress metering, to ensure that the user is
195 * not left staring at an apparently hung, blank window.
197 AppWindowMaker *app = GetAppWindow( GetParent( (HWND)(window) ));
198 SendMessage( (HWND)(window),
199 WM_SETTEXT, 0, (LPARAM)("Loading Package Catalogue")
201 ProgressMeterMaker ui( (HWND)(window), app );
203 /* For this activity, we request automatic dismissal of the dialogue,
204 * when loading has been completed; the user will have an opportunity
205 * to countermand this choice, if loading is delayed by the required
206 * download of any missing local catalogue file.
208 HWND dlg = GetDlgItem( (HWND)(window), IDD_AUTO_CLOSE_OPTION );
209 SendMessage( dlg, WM_SETTEXT, 0,
210 (LPARAM)("Close dialogue automatically, when loading is complete.")
212 CheckDlgButton( (HWND)(window), IDD_AUTO_CLOSE_OPTION, BST_CHECKED );
214 /* We've now set up the initial state for the progress meter dialogue;
215 * proceed to load, (and perhaps download), the XML data files.
217 app->LoadPackageData( false );
219 /* When loading has been completed, automatically dismiss the dialogue...
221 if( IsDlgButtonChecked( (HWND)(window), IDD_AUTO_CLOSE_OPTION ) )
222 SendMessage( (HWND)(window), WM_COMMAND, (WPARAM)(IDOK), 0 );
224 /* ...unless the user has countermanded the automatic dismissal request...
227 { /* ...in which case, we activate the manual dismissal button...
229 if( (dlg = GetDlgItem( (HWND)(window), IDOK )) != NULL )
230 EnableWindow( dlg, TRUE );
232 /* ...and notify the user that it must be clicked to continue.
234 ui.Annotate( "Data has been loaded; please close this dialogue to continue." );
238 static void pkgInvokeUpdate( void *window )
240 /* Thread procedure for performing a package catalogue update.
241 * This will download catalogue files from the remote repository,
242 * and integrate them into the locally cached catalogue XML file
243 * set. Since this is normally a time consuming process, we must
244 * subject it to progress metering, to ensure that the user is
245 * not left staring at an apparently hung, blank window.
247 AppWindowMaker *app = GetAppWindow( GetParent( (HWND)(window) ));
248 ProgressMeterMaker ui( (HWND)(window), app );
250 /* After setting up the progress meter, we clear out any data
251 * which was previously loaded into the package list, reload it
252 * with the "forced download" option, and refresh the display.
254 app->ClearPackageList();
255 app->LoadPackageData( true );
256 app->UpdatePackageList();
258 /* During the update, the user may have selected the option for
259 * automatic dismissal of the dialogue box on completion...
261 if( IsDlgButtonChecked( (HWND)(window), IDD_AUTO_CLOSE_OPTION ) )
263 * ...in which case, we dismiss it without further ado...
265 SendMessage( (HWND)(window), WM_COMMAND, (WPARAM)(IDOK), 0 );
268 { /* ...otherwise, we activate the manual dismissal button...
270 HWND dlg = GetDlgItem( (HWND)(window), IDOK );
271 if( dlg != NULL ) EnableWindow( dlg, TRUE );
273 /* ...and notify the user that it must be clicked to continue.
275 ui.Annotate( "Update is complete; please close this dialogue to continue." );
279 inline void AppWindowMaker::ExecuteScheduledActions( void )
281 /* Helper method to delegate execution of a schedule of actions from
282 * the application window to its associated action item controller.
284 pkgData->Schedule()->Execute( false );
285 pkgData->UpdateSystemMap();
288 class pkgDialogueSpinWait: public pkgSpinWait
290 /* Derivative class for redirection of pkgSpinWait::Report()
291 * messages to a specified dialogue box text control.
294 pkgDialogueSpinWait( HWND msg ): dlg( msg ){}
297 virtual int DispatchReport( const char *, va_list );
301 int pkgDialogueSpinWait::DispatchReport( const char *fmt, va_list argv )
303 /* Method to handle pkgSpinWait::Report() message redirection.
305 char buf[ 1 + vsnprintf( NULL, 0, fmt, argv ) ];
306 int count = vsnprintf( buf, sizeof( buf ), fmt, argv );
307 SendMessage( dlg, WM_SETTEXT, 0, (LPARAM)(buf) );
311 static void pkgApplyChanges( void *window )
313 /* Worker thread processing function, run while displaying the
314 * IDD_APPLY_EXECUTE dialogue box, to apply scheduled changes.
316 HWND msg = GetDlgItem( (HWND)(window), IDD_PROGRESS_MSG );
317 AppWindowMaker *app = GetAppWindow( GetParent( (HWND)(window) ) );
319 /* Set up progess reporting and diagnostic message display
320 * channels, and execute the scheduled actions.
322 pkgDialogueSpinWait stat( msg );
323 dmh_setpty( GetDlgItem( (HWND)(window), IDD_DMH_CONSOLE ) );
324 app->ExecuteScheduledActions();
327 /* Check for successful application of all scheduled changes.
329 int error_count = app->EnumerateActions( ACTION_UNSUCCESSFUL );
331 /* During processing, the user may have selected the option for
332 * automatic dismissal of the dialogue box on completion...
334 if( (error_count == 0)
335 && IsDlgButtonChecked( (HWND)(window), IDD_AUTO_CLOSE_OPTION ) )
337 * ...in which case, and provided all changes were applied
338 * successfully, we dismiss it without further ado...
340 SendMessage( (HWND)(window), WM_COMMAND, (WPARAM)(IDOK), 0 );
343 { /* ...otherwise, we activate the manual dismissal button...
346 if( (dlg = GetDlgItem( (HWND)(window), IDOK )) != NULL )
347 EnableWindow( dlg, TRUE );
349 /* ...and notify the user that it must be clicked to continue.
351 stat.Report( (error_count == 0)
352 ? "All changes were applied successfully;"
353 " you may now close this dialogue."
354 : "Not all changes were applied successfully;"
355 " please refer to details below."
360 static int CALLBACK pkgDialogue
361 ( HWND window, unsigned int msg, WPARAM wParam, LPARAM lParam )
363 /* Generic handler for dialogue boxes which delegate an associated
364 * processing activity to a background thread.
368 /* We need to handle only two classes of windows messages
369 * on behalf of such dialogue boxes...
372 /* ...viz. on initial dialogue box creation, we delegate the
373 * designated activity to the background thread...
375 _beginthread( AppWindowMaker::DialogueThread, 0, (void *)(window) );
379 if( LOWORD( wParam ) == IDOK )
381 /* ...then we wait for a notification that the dialogue may be
382 * closed, (which isn't permitted until the thread completes).
384 EndDialog( window, 0 );
388 /* Any other messages, which are directed to this dialogue box,
389 * may be safely ignored.
394 /* The following static member of the AppWindowMaker class is used
395 * to pass the function reference for the worker thread process to
396 * the preceding dialogue box handler function, when it is invoked
397 * by the following helper method.
399 pkgDialogueThread *AppWindowMaker::DialogueThread = NULL;
401 int AppWindowMaker::DispatchDialogueThread( int id, pkgDialogueThread *handler )
403 /* Helper method to open a dialogue box, and to initiate a worker
404 * thread to handle a designated background process on its behalf.
406 DialogueThread = handler;
407 return DialogueResponse( id, pkgDialogue );
410 inline unsigned long AppWindowMaker::EnumerateActions( int classified )
412 /* Helper method to enumerate the actions of a specified
413 * class, within the current schedule.
415 return pkgData->Schedule()->EnumeratePendingActions( classified );
418 inline pkgActionItem *pkgActionItem::SuppressRedundantUpgrades( void )
420 /* Helper method to adjust the schedule of upgrades, after marking
421 * all installed packages, to exclude all those which are already at
422 * the most recently available release.
425 if( (head = this) != NULL )
427 /* First, provided the schedule is not empty, we walk the list
428 * of scheduled actions, until we find the true first entry...
430 while( head->prev != NULL ) head = head->prev;
431 for( pkgActionItem *ref = head; ref != NULL; ref = ref->next )
433 /* ...and then, we process the list from first entry to last,
434 * selecting those entries which schedule an upgrade action...
436 if( ((ref->flags & ACTION_MASK) == ACTION_UPGRADE)
438 * ...and for which the currently installed release is the
439 * same as that which an upgrade would install...
441 && (ref->selection[ to_install ] == ref->selection[ to_remove ]) )
443 * ...in which case, we mark this entry for "no action".
445 ref->flags &= ~ACTION_MASK;
448 /* Finally, we return a pointer to the first entry in the schedule.
453 static int pkgActionCount( HWND dlg, int id, const char *fmt, int classified )
455 /* Helper function to itemise the currently scheduled actions
456 * of a specified class, recording the associated package name
457 * within the passed EDITTEXT control, and noting the count of
458 * itemised actions within the heading of the control.
460 * First, count the actions, while adding the package names
461 * to the edit control with the specified ID.
463 dmh_setpty( GetDlgItem( dlg, id ) );
464 int count = GetAppWindow( GetParent( dlg ))->EnumerateActions( classified );
466 /* Construct the heading, according to the specified format,
467 * and including the identified action count.
469 const char *packages = (count == 1) ? "package" : "packages";
470 char label_text[1 + snprintf( NULL, 0, fmt, count, packages )];
471 snprintf( label_text, sizeof( label_text), fmt, count, packages );
473 /* Finally, update the heading on the edit control, assigning
474 * it to the dialogue control with ID one greater than that of
475 * the edit control itself.
477 SendMessage( GetDlgItem( dlg, ++id ), WM_SETTEXT, 0, (LPARAM)(label_text) );
482 /* Implement a macro as shorthand notation for passing of action
483 * specific argument lists to the pkgActionCount() function...
485 #define ACTION_APPLY(OP) APPLIES_TO(OP), FMT_APPLY_##OP##S, ACTION_##OP
486 #define APPLIES_TO(OP) IDD_APPLY_##OP##S_PACKAGES
488 /* ...using the appropriate selection from these action specific
491 #define FMT_APPLY_REMOVES "%u installed %s will be removed"
492 #define FMT_APPLY_UPGRADES "%u installed %s will be upgraded"
493 #define FMT_APPLY_INSTALLS "%u new/upgraded %s will be installed"
495 static int CALLBACK pkgApplyApproved
496 ( HWND window, unsigned int msg, WPARAM wParam, LPARAM lParam )
498 /* Callback function servicing the custom dialogue box in which
499 * scheduled actions are itemised for user confirmation, prior
503 { case WM_INITDIALOG:
504 /* On opening the dialogue box, itemise each of the
505 * remove, upgrade, and install action classes.
507 pkgActionCount( window, ACTION_APPLY( REMOVE ) );
508 pkgActionCount( window, ACTION_APPLY( UPGRADE ) );
509 pkgActionCount( window, ACTION_APPLY( INSTALL ) );
513 /* Wait for the user to review the itemised schedule
514 * of pending changes, confirm intent to proceed...
516 long opt = LOWORD( wParam );
517 if( (opt == ID_APPLY) || (opt == ID_DISCARD) || (opt == ID_DEFER) )
519 /* ...then close the dialogue, passing the selected
520 * continuation option back to the caller.
522 EndDialog( window, opt );
526 /* Ignore any window messages we don't recognise.
531 int AppWindowMaker::Invoked( void )
533 /* Override for the WTK::MainWindowMaker::Invoked() method; it
534 * provides the hook for the initial loading of the XML database,
535 * and creation of the display controls through which its content
536 * will be presented to the user, prior to invocation of the main
537 * window's message loop.
539 * The data displays depend on the MS-Windows Common Controls API;
540 * initialise all components of this up front.
542 InitCommonControls();
544 /* Load the data from the XML catalogue files; this activity
545 * is invoked in a background thread, initiated from a progress
546 * dialogue derived from the "Update Catalogue" template.
548 DispatchDialogueThread( IDD_REPO_UPDATE, pkgInvokeInitDataLoad );
550 /* Establish the initial views of the package category selection
551 * tree, and the list of available packages; (the initial package
552 * list includes everything in the "All Packages" category).
554 InitPackageTreeView();
555 InitPackageListView();
557 /* Initialise the data-sheet tab control, displaying the default
558 * "no package selected" message.
560 InitPackageTabControl();
562 /* Force a layout adjustment, to ensure that the displayed
563 * data controls are correctly populated.
566 InvalidateRect( AppWindow, NULL, FALSE );
567 UpdateWindow( AppWindow );
569 /* Finally, we may delegate all further processing to the main
570 * window's message loop.
572 return WTK::MainWindowMaker::Invoked();
575 /* The following static entities are provided to facilitate the
576 * implementation of the "discard changes" confirmation dialogue.
578 static const char *pkgConfirmAction = NULL;
579 static AppWindowMaker *pkgConfirmActionClient = NULL;
581 static void dlgReplaceText( HWND dlg, ... )
583 /* Local helper routine, used by AppWindowMaker::ConfirmActionDialogue(),
584 * to substitute the designated action description into the format strings,
585 * as specified within the dialogue box template.
587 * First step is to copy the format specification from the dialogue item.
589 int len = 1 + SendMessage( dlg, WM_GETTEXTLENGTH, 0, 0 );
590 char fmt[len]; SendMessage( dlg, WM_GETTEXT, len, (LPARAM)(fmt) );
592 /* Having established the format, we allocate a sufficient buffer, in
593 * which we prepare the formatted text...
595 va_list argv; va_start( argv, dlg );
596 char text[1 + vsnprintf( NULL, 0, fmt, argv )];
597 vsnprintf( text, sizeof( text ), fmt, argv );
600 /* ...which we then copy back to the active dialogue item.
602 SendMessage( dlg, WM_SETTEXT, 0, (LPARAM)(text) );
605 int CALLBACK AppWindowMaker::ConfirmActionDialogue
606 ( HWND dlg, unsigned int msg, WPARAM wparam, LPARAM lparam )
607 #define DIALOGUE_CHKSTATE pkgConfirmActionClient->EnumerateActions
608 #define DIALOGUE_DISPATCH pkgConfirmActionClient->DispatchDialogueThread
609 #define DIALOGUE_RESPONSE pkgConfirmActionClient->DialogueResponse
611 /* Call back handler for the messages generated by the "discard changes"
612 * confirmation dialogue.
615 { case WM_INITDIALOG:
616 /* When the confirmation dialogue is invoked, we centre it within
617 * its owner window, and replace the format specification strings
618 * from its text controls with their formatted equivalents.
620 WTK::AlignWindow( dlg, WTK_ALIGN_CENTRED );
621 dlgReplaceText( GetDlgItem( dlg, IDD_PROGRESS_MSG ), pkgConfirmAction );
622 dlgReplaceText( GetDlgItem( dlg, IDD_PROGRESS_TXT ), pkgConfirmAction,
628 /* Process the messages from the dialogue's three command buttons:
630 switch( msg = LOWORD( wparam ) )
633 /* Sent when the user selects the "Discard Changes" option,
634 * we return "IDOK" to confirm that the action should be
635 * progressed, ignoring any uncommitted changes.
637 EndDialog( dlg, IDOK );
641 /* Sent when the user selects the "Review Changes" option; we
642 * shelve the active dialogue, and delegate the decision as to
643 * whether to proceed to the "Apply Changes" dialogue.
645 ShowWindow( dlg, SW_HIDE );
646 switch( DIALOGUE_RESPONSE( IDD_APPLY_APPROVE, pkgApplyApproved ) )
649 /* Handle a "Defer" request from the "Apply Changes"
650 * dialogue as equivalent to our own "Cancel Request"
657 /* When "Apply" confirmation is forthcoming, we proceed to
658 * download any required packages, and invoke the scheduled
659 * remove, upgrade, or install actions.
661 DIALOGUE_DISPATCH( IDD_APPLY_DOWNLOAD, pkgInvokeDownload );
662 DIALOGUE_DISPATCH( IDD_APPLY_EXECUTE, pkgApplyChanges );
664 /* Ensure that all "Apply" actions completed successfully...
666 if( DIALOGUE_CHKSTATE( ACTION_UNSUCCESSFUL ) > 0 )
668 * ...otherwise, cancel the ensuing action, so that the
669 * user may have an opportunity to remedy the problem.
673 /* Regardless of the outcome from the "Review Changes" activity,
674 * simply fall through to terminate the dialogue appropriately.
677 /* Sent when the user selects the "Cancel Request" option; we
678 * simply allow the message ID to propagate back to the caller,
679 * as the response code.
681 EndDialog( dlg, msg );
685 /* We don't handle any other message; if any is received, mark it as
686 * "unhandled", and pass it on to the default dialogue handler.
691 bool AppWindowMaker::ConfirmActionRequest( const char *desc )
693 /* Helper to confirm user's intent to proceed, when a request
694 * to update the catalogue, or to quit, would result in pending
695 * installation actions being discarded.
697 * We begin by saving reference data to be passed to the static
698 * dialogue handler method...
700 pkgConfirmAction = desc;
701 pkgConfirmActionClient = this;
703 /* ...prior to checking for pending actions, and invoking the
704 * dialogue procedure when appropriate, or simply allowing the
705 * action to proceed, otherwise.
707 pkgActionItem *pending = pkgData->Schedule();
708 return (pending && pending->EnumeratePendingActions() > 0)
709 ? DialogBox( AppInstance, MAKEINTRESOURCE( IDD_CONFIRMATION ),
710 AppWindow, ConfirmActionDialogue
715 long AppWindowMaker::OnCommand( WPARAM cmd )
716 #define ACTION_PRESERVE_FAILED (ACTION_DOWNLOAD_FAILED | ACTION_APPLY_FAILED)
718 /* Handler for WM_COMMAND messages which are directed to the
719 * top level application window.
722 { case IDM_HELP_ABOUT:
723 /* This request is initiated by selecting "About ...", ...
725 case IDM_HELP_LEGEND:
726 /* ...and this one by selecting "Icon Legend", from the "Help"
727 * menu; in both cases, we respond by displaying an associated
730 WTK::GenericDialogue( AppInstance, AppWindow, cmd );
733 case IDM_PACKAGE_INSTALL:
734 /* Initiated by selecting the "Mark for Installation" option
735 * from the "Package" menu, this request will schedule the
736 * currently selected package, and any currently unfulfilled
737 * dependencies, for installation.
739 Schedule( ACTION_INSTALL );
742 case IDM_PACKAGE_UPGRADE:
743 /* Initiated by selecting the "Mark for Upgrade" option
744 * from the "Package" menu, this request will schedule the
745 * currently selected package, and any currently unfulfilled
746 * dependencies, for upgrade or installation, as appropriate.
748 Schedule( ACTION_UPGRADE );
751 case IDM_PACKAGE_REMOVE:
752 /* Initiated by selecting the "Mark for Removal" option
753 * from the "Package" menu, this request will schedule the
754 * currently selected package for removal.
756 Schedule( ACTION_REMOVE );
759 case IDM_PACKAGE_UNMARK:
760 /* Initiated by selecting the "Unmark" option from the
761 * "Package" menu, this request will cancel the effect of
762 * any previously scheduled action, in respect of the
763 * currently selected package.
765 UnmarkSelectedPackage();
768 case IDM_REPO_UPDATE:
769 /* This request is initiated by selecting "Update Catalogue"
770 * from the "Repository" menu; we respond by initiating a progress
771 * dialogue, from which a background thread is invoked to download
772 * fresh copies of the package catalogue files from the remote
773 * repository, and consolidate them into the local catalogue.
775 * Note that this activity will cause any pending installation
776 * actions to be discarded; seek confirmation of user's intent
777 * to proceed, when this situation prevails.
779 if( ConfirmActionRequest( "update the catalogue" ) )
780 { DispatchDialogueThread( IDD_REPO_UPDATE, pkgInvokeUpdate );
781 UpdatePackageMenuBindings();
785 case IDM_REPO_MARK_UPGRADES:
786 /* Initiated when the user selects the "Mark All Upgrades"
787 * option; in this case, we identify all packages which are
788 * already installed, and for which upgrades are available,
789 * and schedule an upgrade action in respect of each.
791 pkgData->RescheduleInstalledPackages( ACTION_UPGRADE );
793 /* After scheduling all available upgrades, we must
794 * update the package list view marker icons...
796 pkgListViewMaker pkglist( PackageListView );
797 pkglist.MarkScheduledActions(
798 pkgData->Schedule()->SuppressRedundantUpgrades()
801 /* ...and also adjust the menu bindings accordingly.
803 UpdatePackageMenuBindings();
807 /* Initiated when the user selects the "Apply Changes" option,
808 * we first reset the error trapping and download request state
809 * for all scheduled actions, then we present the user with a
810 * dialogue requesting confirmation of approval to proceed.
812 pkgData->Schedule()->Assert( 0UL, ~ACTION_PRESERVE_FAILED );
813 switch( DialogueResponse( IDD_APPLY_APPROVE, pkgApplyApproved ) )
815 /* Of the three possible responses, we simply ignore the "Defer"
816 * option, (since it requires no action), but we must explicitly
817 * handle the "Apply" and "Discard" options.
820 /* When "Apply" confirmation is forthcoming, we proceed to
821 * download any required packages, and invoke the scheduled
822 * remove, upgrade, or install actions.
824 DispatchDialogueThread( IDD_APPLY_DOWNLOAD, pkgInvokeDownload );
825 DispatchDialogueThread( IDD_APPLY_EXECUTE, pkgApplyChanges );
827 /* After applying changes, we fall through...
830 /* ...so that on explicit "Discard" selection by the user,
831 * or following application of all scheduled actions, as a
832 * result of processing the "Apply" selection, we clear the
833 * actions schedule, remove all marker icons, and refresh
834 * the package list to reflect current status.
836 pkgListViewMaker pkglist( PackageListView );
837 pkglist.UpdateListView();
839 /* Updating the list view clears pending action marks from
840 * every entry, but clearing the schedule may not cancel any
841 * request relating to a failed action; restore marked state
842 * for such residual actions.
844 pkglist.MarkScheduledActions(
845 pkgData->ClearScheduledActions( ACTION_PRESERVE_FAILED )
848 /* Clearing the schedule of actions may also affect the
849 * validity of menu options; update accordingly.
851 UpdatePackageMenuBindings();
856 /* This request is initiated by selecting the "Quit" option
857 * from the "Repository" menu; we respond by sending a WM_CLOSE
858 * message, to terminate the current application instance.
860 SendMessage( AppWindow, WM_CLOSE, 0, 0L );
863 /* Any other message is silently ignored.
868 /* $RCSfile$: end of file */