00001 // $Id: progress.cpp 1282 2006-06-09 09:46:49Z alex $ 00002 /* @@tag:xara-cn@@ DO NOT MODIFY THIS LINE 00003 ================================XARAHEADERSTART=========================== 00004 00005 Xara LX, a vector drawing and manipulation program. 00006 Copyright (C) 1993-2006 Xara Group Ltd. 00007 Copyright on certain contributions may be held in joint with their 00008 respective authors. See AUTHORS file for details. 00009 00010 LICENSE TO USE AND MODIFY SOFTWARE 00011 ---------------------------------- 00012 00013 This file is part of Xara LX. 00014 00015 Xara LX is free software; you can redistribute it and/or modify it 00016 under the terms of the GNU General Public License version 2 as published 00017 by the Free Software Foundation. 00018 00019 Xara LX and its component source files are distributed in the hope 00020 that it will be useful, but WITHOUT ANY WARRANTY; without even the 00021 implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 00022 See the GNU General Public License for more details. 00023 00024 You should have received a copy of the GNU General Public License along 00025 with Xara LX (see the file GPL in the root directory of the 00026 distribution); if not, write to the Free Software Foundation, Inc., 51 00027 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 00028 00029 00030 ADDITIONAL RIGHTS 00031 ----------------- 00032 00033 Conditional upon your continuing compliance with the GNU General Public 00034 License described above, Xara Group Ltd grants to you certain additional 00035 rights. 00036 00037 The additional rights are to use, modify, and distribute the software 00038 together with the wxWidgets library, the wxXtra library, and the "CDraw" 00039 library and any other such library that any version of Xara LX relased 00040 by Xara Group Ltd requires in order to compile and execute, including 00041 the static linking of that library to XaraLX. In the case of the 00042 "CDraw" library, you may satisfy obligation under the GNU General Public 00043 License to provide source code by providing a binary copy of the library 00044 concerned and a copy of the license accompanying it. 00045 00046 Nothing in this section restricts any of the rights you have under 00047 the GNU General Public License. 00048 00049 00050 SCOPE OF LICENSE 00051 ---------------- 00052 00053 This license applies to this program (XaraLX) and its constituent source 00054 files only, and does not necessarily apply to other Xara products which may 00055 in part share the same code base, and are subject to their own licensing 00056 terms. 00057 00058 This license does not apply to files in the wxXtra directory, which 00059 are built into a separate library, and are subject to the wxWindows 00060 license contained within that directory in the file "WXXTRA-LICENSE". 00061 00062 This license does not apply to the binary libraries (if any) within 00063 the "libs" directory, which are subject to a separate license contained 00064 within that directory in the file "LIBS-LICENSE". 00065 00066 00067 ARRANGEMENTS FOR CONTRIBUTION OF MODIFICATIONS 00068 ---------------------------------------------- 00069 00070 Subject to the terms of the GNU Public License (see above), you are 00071 free to do whatever you like with your modifications. However, you may 00072 (at your option) wish contribute them to Xara's source tree. You can 00073 find details of how to do this at: 00074 http://www.xaraxtreme.org/developers/ 00075 00076 Prior to contributing your modifications, you will need to complete our 00077 contributor agreement. This can be found at: 00078 http://www.xaraxtreme.org/developers/contribute/ 00079 00080 Please note that Xara will not accept modifications which modify any of 00081 the text between the start and end of this header (marked 00082 XARAHEADERSTART and XARAHEADEREND). 00083 00084 00085 MARKS 00086 ----- 00087 00088 Xara, Xara LX, Xara X, Xara X/Xtreme, Xara Xtreme, the Xtreme and Xara 00089 designs are registered or unregistered trademarks, design-marks, and/or 00090 service marks of Xara Group Ltd. All rights in these marks are reserved. 00091 00092 00093 Xara Group Ltd, Gaddesden Place, Hemel Hempstead, HP2 6EX, UK. 00094 http://www.xara.com/ 00095 00096 =================================XARAHEADEREND============================ 00097 */ 00098 00099 // The progress indicator, for slow operations (kernel implementation). 00100 00101 00102 #include "camtypes.h" 00103 00104 //#include "app.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00105 #include "csrstack.h" 00106 //#include "jason.h" 00107 #include "keypress.h" 00108 //#include "mainfrm.h" 00109 //#include "monotime.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00110 //#include "oilprog.h" 00111 //#include "progbar.h" 00112 //#include "node.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00113 #include "statline.h" 00114 00115 #include "progress.h" 00116 00117 00118 DECLARE_SOURCE("$Revision: 1282 $"); 00119 00120 CC_IMPLEMENT_MEMDUMP(Progress, CC_CLASS_MEMDUMP) 00121 00122 #define new CAM_DEBUG_NEW 00123 00124 00125 BOOL Progress::AbortJob = FALSE; // TRUE if the we want to abort a job in ralph 00126 00127 00128 #if !defined(EXCLUDE_FROM_RALPH) 00129 00130 // --- Thread activation - set to 1 to enable the delayed-show thread 00131 // (NOTE: The start-delay thread is currently non-operational, so turning it on 00132 // has no effect. See below for details) 00133 #define DELAY_THREAD 0 00134 00135 // --- Debugging trace statements. Set to 1 to turn on debug output 00136 #define DEBUG_OUTPUT 0 00137 00138 00139 // --- Constants 00140 // IMMEDIATE REDRAW 00141 // const INT32 STARTDELAY = 3333; 00142 const INT32 STARTDELAY = 333; // Default delay before cursor appears (1/3rd sec) 00143 00144 00145 00146 /******************************************************************************************** 00147 00148 > BOOL BeginSlowJob(INT32 finalcount = -1, BOOL delay = TRUE, String_64 *Description = NULL) 00149 Purpose: DEPRECATED - do not call this function! 00150 SeeAlso: Progress::Start; Progress::Progress 00151 00152 ********************************************************************************************/ 00153 00154 BOOL BeginSlowJob(INT32 finalcount /* =-1 */, BOOL delay /* = TRUE */, 00155 String_64 *Description /* = NULL */) 00156 { 00157 Progress::Start(delay, Description, finalcount); 00158 00159 return(TRUE); 00160 } 00161 00162 00163 00164 /******************************************************************************************** 00165 00166 > BOOL ContinueSlowJob(INT32 upto = 0) 00167 Purpose: DEPRECATED - do not call this function! 00168 SeeAlso: Progress::Update 00169 00170 ********************************************************************************************/ 00171 00172 BOOL ContinueSlowJob(INT32 upto) 00173 { 00174 return(Progress::Update(upto)); 00175 } 00176 00177 00178 00179 /******************************************************************************************** 00180 00181 > void EndSlowJob(void) 00182 Purpose: DEPRECATED - do not call this function! 00183 SeeAlso: Progress::Stop 00184 00185 ********************************************************************************************/ 00186 00187 void EndSlowJob(void) 00188 { 00189 Progress::Stop(); 00190 } 00191 00192 00193 00194 /******************************************************************************************** 00195 00196 > void SmashSlowJob(void) 00197 Purpose: DEPRECATED - do not call this function! 00198 SeeAlso: Progress::Smash 00199 00200 ********************************************************************************************/ 00201 00202 void SmashSlowJob(void) 00203 { 00204 Progress::Smash(); 00205 } 00206 00207 00208 00209 00210 00211 00212 00213 00214 00215 00216 00217 // ----------------------------------------------------------------------------------------- 00218 // --- The new Progress class - firstly the static threading support 00219 00220 #if DELAY_THREAD 00221 // Thread control 00222 volatile static BOOL ThreadAlive = FALSE; // TRUE while the thread is alive - set to FALSE to kill it 00223 00224 00225 /******************************************************************************************** 00226 00227 > static UINT32 ThreadProc(LPVOID Param) 00228 00229 Author: Jason_Williams (Xara Group Ltd) <camelotdev@xara.com> 00230 Date: 10/11/95 00231 00232 Purpose: This function is the progress system's delayed-show thread. It runs 00233 alongside the main program, sleeping for 1/3rd of a second. After this 00234 delay period, it calls Progress::ThreadCallback. 00235 00236 Notes: While this thread is running, ThreadAlive will be TRUE. 00237 Set ThreadAlive to FALSE to kill the thread. 00238 00239 Scope: static/private in winoil\progress.cpp 00240 00241 SeeAlso: Progress::ThreadCallback 00242 00243 ********************************************************************************************/ 00244 00245 static UINT32 ThreadProc(LPVOID Param) 00246 { 00247 // If there's already an active thread, then we'll exit immediately 00248 if (ThreadAlive) 00249 return(0); 00250 00251 // Flag the fact that we are alive 00252 ThreadAlive = TRUE; 00253 00254 // While we're alive, update the progress display periodically. 00255 // The main program thread will set ThreadAlive=FALSE if they want us to stop 00256 while (ThreadAlive) 00257 { 00258 Sleep(STARTDELAY); 00259 00260 if (ThreadAlive) // Check again as the value may have changed while we slept 00261 Progress::ThreadCallback(); 00262 } 00263 00264 return(0); 00265 } 00266 #endif 00267 00268 00269 00270 /******************************************************************************************** 00271 00272 > static void Progress::ThreadCallback(void) 00273 00274 Author: Jason_Williams (Xara Group Ltd) <camelotdev@xara.com> 00275 Date: 10/11/95 00276 00277 Purpose: This method is called by our delay thread to show any delayed progress 00278 displays that we wish to display. It should not be called from elsewhere. 00279 00280 Notes: There are 2 progress displays - the hourglass and progress bar. There is no 00281 point in showing the bar until there is a percentage to display, so this 00282 function only turns on the hourglass cursor. 00283 00284 NOTE also that if you change this method do anything more than this, you 00285 must be very careful - generally the thread can introduce instability 00286 that causes random access violations and stuff. 00287 00288 Scope: public, but don't let that fool you. It'd be private but for a technical hitch. 00289 00290 SeeAlso: ::ThreadProc 00291 00292 ********************************************************************************************/ 00293 00294 void Progress::ThreadCallback(void) 00295 { 00296 #if DELAY_THREAD 00297 //-- This does not work. The SetCursor call is executed, but the cursor stubbornly 00298 //-- refuses to change shape - I suspect this is because the window under the cursor 00299 //-- belongs to the main process thread, not us... 00300 00301 // Tell the thread that it can exit now 00302 ThreadAlive = FALSE; 00303 00304 // And change the cursor shape to an hourglass 00305 // NOTE that I don't call BeginBusyCursor here, to avoid the cursor stack 00306 // doing anything - that way the busy pointer will happily go away as soon as anyone 00307 // tries to do anything with the Camelot cursor stack. 00308 HCURSOR Busy = ::LoadCursor(NULL, _R(IDC_WAIT)); 00309 ::SetCursor(Busy); 00310 #endif 00311 } 00312 00313 00314 00315 00316 // ----------------------------------------------------------------------------------------- 00317 // --- The new Progress class 00318 00319 // --- Statics 00320 INT32 Progress::ActiveDisplays = 0; // A count of the number of active progress incarnations 00321 BOOL Progress::JobCancelled = FALSE; // TRUE if the job has been cancelled 00322 00323 BOOL Progress::JobDelayed = FALSE; // TRUE if the job has a delayed-start 00324 00325 INT32 Progress::FinalCount = 0; // The number we're currently counting up to 00326 INT32 Progress::CurrentPercent = 0; // The number we're currently counting up to 00327 00328 BOOL Progress::HourglassShown = FALSE; // TRUE if we have already shown the hourglass 00329 00330 String_64 Progress::JobDescription = TEXT(""); // A description of the current job 00331 BOOL Progress::HaveJobDescription = FALSE; // TRUE if JobDescription is a proper (caller supplied) one 00332 00333 MonotonicTime Progress::StartTime; // Time the first call to Show() was made 00334 00335 //CProgressBar *Progress::ProgBar = NULL; // The progress bar object (if used) 00336 00337 00338 /******************************************************************************************** 00339 00340 Preference: ProgressBar 00341 Section: Displays 00342 Range: TRUE or FALSE 00343 Purpose: If TRUE, a progress bar will be displayed while Camelot is busy 00344 processing. 00345 SeeAlso: Hourglass 00346 00347 ********************************************************************************************/ 00348 00349 INT32 Progress::DisplayBar = TRUE; // Preference: Is progress bar shown? 00350 00351 00352 00353 00354 00355 /******************************************************************************************** 00356 00357 > Progress::Progress() 00358 00359 Author: Jason_Williams (Xara Group Ltd) <camelotdev@xara.com> 00360 Date: 10/11/95 00361 00362 Purpose: Constructs an instance of a Progress object. 00363 This starts the hourglass running. 00364 Destruct the object to stop the hourglass. 00365 00366 See Progress::Start() for more details on the progress system 00367 00368 Notes: IMPORTANT! 00369 Only use this constructor for small, deep-down functions which you can't 00370 supply a useful operation description for. Whenever we're busy we must 00371 endeavour to show a useful description to the user. 00372 00373 SeeAlso: Progress::~Progress; Progress::Start 00374 00375 ********************************************************************************************/ 00376 00377 Progress::Progress() 00378 { 00379 ProgressCount = 0; 00380 00381 Progress::Start(TRUE, (StringBase *)NULL, -1); 00382 } 00383 00384 00385 00386 /******************************************************************************************** 00387 00388 > Progress::Progress(StringBase *Description, INT32 FinalCount,BOOL Delay=TRUE, BOOL bEnable = TRUE) 00389 > Progress::Progress(UINT32 DescriptionID, INT32 FinalCount,BOOL Delay=TRUE, BOOL bEnable = TRUE) 00390 00391 Author: Jason_Williams (Xara Group Ltd) <camelotdev@xara.com> 00392 Date: 10/11/95 00393 00394 Inputs: Description - A pointer to a string describing what the program is busy doing 00395 or DescriptionID - The string Resource ID of the string to use 00396 00397 FinalCount - -1 if you want no progress bar (percentage) indication, else 00398 the number you expect to count up to (e.g. number of lines 00399 in the file you're reading, number of nodes you're changing, etc) 00400 00401 Delay - TRUE if you want the hourglass to appear after a short delay 00402 FALSE for it to appear immediately 00403 00404 Purpose: Constructs an instance of a Progress object. 00405 This starts the hourglass (and optional progress bar) running. 00406 Destruct the object to stop the hourglass. 00407 00408 See Progress::Start() for more details on the progress system 00409 00410 SeeAlso: Progress::~Progress; Progress::Start; Progress::Update 00411 00412 ********************************************************************************************/ 00413 00414 Progress::Progress(StringBase *Description, INT32 FinalCount,BOOL Delay, BOOL bEnable) 00415 { 00416 if (bEnable) 00417 { 00418 ProgressCount = 0; 00419 ERROR3IF(Description == NULL, "Progress constructor - Illegal NULL parameter"); 00420 Progress::Start(Delay, Description, FinalCount); 00421 } 00422 } 00423 00424 00425 Progress::Progress(UINT32 DescriptionID, INT32 FinalCount,BOOL Delay, BOOL bEnable) 00426 { 00427 if (bEnable) 00428 { 00429 ProgressCount = 0; 00430 ERROR3IF(!DescriptionID, "Progress constructor - Illegal NULL parameter"); 00431 Progress::Start(Delay, DescriptionID, FinalCount); 00432 } 00433 } 00434 00435 00436 00437 /******************************************************************************************** 00438 00439 > Progress::~Progress() 00440 00441 Author: Jason_Williams (Xara Group Ltd) <camelotdev@xara.com> 00442 Date: 10/11/95 00443 00444 Purpose: Destructs an instance of a Progress object. 00445 This stops the hourglass (and optional progress bar) which the constructor 00446 started going. 00447 00448 SeeAlso: Progress::Progress; Progress::Stop 00449 00450 ********************************************************************************************/ 00451 00452 Progress::~Progress() 00453 { 00454 Progress::Stop(); 00455 } 00456 00457 00458 /******************************************************************************************** 00459 00460 > BOOL Progress::SetDescription(const StringBase* const Description) 00461 00462 Author: Colin_Barfoot (Xara Group Ltd) <camelotdev@xara.com> 00463 Date: 18/12/96 00464 00465 Inputs: Description: See Start() for a description of this parameter 00466 00467 Purpose: Gives the user some textual description of what's going on when the hourglass 00468 is blocking the view. 00469 00470 ********************************************************************************************/ 00471 BOOL Progress::SetDescription(const StringBase* const Description) 00472 { 00473 Description->Left(&JobDescription, 63); 00474 HaveJobDescription = TRUE; 00475 00476 return TRUE; 00477 } 00478 00479 00480 /******************************************************************************************** 00481 00482 > void Progress::Start(BOOL Delay, StringBase *Description, INT32 FinalCount) 00483 > void Progress::Start(BOOL Delay, UINT32 DescriptionID, INT32 FinalCount) 00484 00485 Author: Jason_Williams (Xara Group Ltd) <camelotdev@xara.com> 00486 Date: 10/11/95 00487 00488 Inputs: Delay - TRUE to start the hourglass after a 1/3rd second delay 00489 FALSE to show the hourglass immediately [see Notes] 00490 00491 Description - NULL ("Processing... please wait") or a pointer to a string 00492 describing what the program is currently busy doing 00493 or DescriptionID - NULL or the string Resource ID of the string to use 00494 00495 FinalCount - -1 if you want no progress bar (percentage) indication, else 00496 the number you expect to count up to (e.g. number of lines 00497 in the file you're reading, number of nodes you're changing, etc) 00498 00499 Purpose: [NOTE that to show a progress display, the easiest system is to construct 00500 a Progress object - this will automatically turn the hourglass off when the 00501 object is destructed. Only use this call if you need lower level control 00502 or it is a lot more convenient than a simple instance of the class] 00503 00504 Starts a progress (hourglass and optional progress bar) indication. Call this 00505 when you start doing something that could take a significant amount of time 00506 (more than half a second delay should show an hourglass under all circumstances) 00507 00508 Notes: The delay parameter specifies whether the hourglass should be shown immediately 00509 or if its appearance should be put off for a short time in case you don't take 00510 as long as you thought you might. NOTE however, that you *must* call 00511 Progress::Update at frequent intervals or the delayed hourglass may never appear! 00512 Note that Progress::Update is very efficient, so may be called very often (it 00513 only updates 6 times a second at the most). 00514 00515 IMPORTANT - The description should be filled in with an appropriate string. 00516 We must avoid use of the default generic description whenever possible. 00517 00518 Multiple progress indications may be active at any time. In this case, the 00519 calls to Start and Stop are "stacked" so the hourglass will only go away when 00520 all Start calls have been countermanded by Stops. Preferably, the outermost 00521 Start/Stop will provide a progress percentage, while the inner ones will 00522 simply use Start() with the default parameters, merely to ensure that an 00523 hourglass is shown while they are busy. Don't rely upon your caller to 00524 show the hourglass for you! The start/stop/update calls have all been designed 00525 to be called often without introducing noticable performance overheads. 00526 00527 SeeAlso: Progress::Start; Progress::Update; Progress::Stop; Progress::Smash; 00528 Progress::Progress 00529 00530 ********************************************************************************************/ 00531 00532 void Progress::Start(BOOL Delay, UINT32 DescriptionID, INT32 IntendedFinalCount) 00533 { 00534 if (!DescriptionID) 00535 Progress::Start(Delay, (StringBase *)NULL, IntendedFinalCount); 00536 else 00537 { 00538 String_256 Description(DescriptionID); 00539 00540 Progress::Start(Delay, (StringBase *)&Description, IntendedFinalCount); 00541 } 00542 } 00543 00544 00545 00546 void Progress::Start(BOOL Delay, StringBase *Description, INT32 IntendedFinalCount) 00547 { 00548 #if DEBUG_OUTPUT 00549 TRACE( _T("Progress::Start ActiveDisplays = %d IntendedFinalCount = %d\n"),ActiveDisplays,IntendedFinalCount); 00550 #endif 00551 00552 // Ensure the ActiveDisplays value is valid 00553 if (ActiveDisplays < 0) 00554 ActiveDisplays = 0; 00555 00556 ActiveDisplays++; 00557 if (ActiveDisplays <= 1) 00558 { 00559 // --- We're starting a brand new progress indication - set everything up 00560 JobDelayed = Delay; 00561 JobCancelled = FALSE; 00562 CurrentPercent = 0; 00563 FinalCount = IntendedFinalCount; 00564 HourglassShown = FALSE; 00565 00566 // Remember when we started and when we last updated the screen displays 00567 StartTime.Sample(); 00568 00569 if (Description != NULL) 00570 { 00571 SetDescription(Description); 00572 } 00573 else 00574 { 00575 JobDescription.MakeMsg(_R(IDS_BUSYMSG)); 00576 HaveJobDescription = FALSE; 00577 } 00578 00579 // Inform the Node subsystem that we're putting up an hourglass 00580 Node::StartHourglass(); 00581 00582 // Update the status line text 00583 GetApplication()->UpdateStatusBarText((String_256 *)&JobDescription, FALSE); 00584 00585 #if DELAY_THREAD 00586 // Attempt to start a thread to show the hourglass after a delay 00587 if (JobDelayed) 00588 ::AfxBeginThread(ThreadProc, NULL); 00589 #endif 00590 } 00591 else 00592 { 00593 // --- We're starting a progress indication while another one is already active. 00594 // If they've got a better idea about what the final count should be, use their estimate 00595 00596 // If an "inner" job wants the hourglass to _not_ be delayed, we override the 00597 // current JobDelayed state to make sure it definitely shows an hourglass 00598 if (!Delay) 00599 JobDelayed = FALSE; 00600 00601 // Removed by Jason, 22/1/96. This causes ensures to go off and bad progress 00602 // displays when you have a case like the following: 00603 // Start (-1) 00604 // Start(100) 00605 // End 00606 // Start(200) 00607 // End 00608 // End 00609 // ...In the 2nd progress bar, the FinalCount remains at 100, so Update error3's 00610 // This occurs in saving/exporting and other multi-part busy processes 00611 // A proper fix for this problem requires proper nesting of hourglasses, 00612 // which is not feasible at this stage (a *lot* of conversion work on 00613 // existing code would be necessary). 00614 // This still won't work if progress bars are nested, but should work 00615 // reasonably well otherwise. 00616 // if (FinalCount <= 0) 00617 // FinalCount = IntendedFinalCount; 00618 // The replacement code for the above now occurs below, after we get the JobDescription 00619 00620 00621 // Similarly, if we have only a generic job desc and they've supplied one, then we'll 00622 // use the one that they supplied. 00623 if (!HaveJobDescription && Description != NULL) 00624 { 00625 SetDescription(Description); 00626 } 00627 00628 // Check if we're starting a new progress-bar job, in which case it overrides 00629 // any previous settings for FinalCount and the description string 00630 if (IntendedFinalCount > 0) 00631 { 00632 FinalCount = IntendedFinalCount; 00633 00634 // And reset the current percentage and job description fields 00635 CurrentPercent = 0; 00636 if (Description != NULL) 00637 { 00638 SetDescription(Description); 00639 } 00640 00641 // And reset/redraw the progress bar display to make sure it starts from 0 again 00642 // and shows the new job description (if any). 00643 if (StatusLine::Get()) 00644 { 00645 StatusLine::Get()->SetPercent(0, TRUE, (HaveJobDescription) ? &JobDescription : NULL); 00646 } 00647 } 00648 } 00649 00650 // And finally, update to show the current state. This will show the progress displays 00651 // if appropriate at this time, etc. 00652 Progress::Update(0); 00653 } 00654 00655 00656 00657 /******************************************************************************************** 00658 00659 > BOOL Progress::Update(INT32 CurrentCount = 0,BOOL FreqEscChecks = FALSE) 00660 00661 Author: Jason_Williams (Xara Group Ltd) <camelotdev@xara.com> 00662 Date: 10/11/95 00663 00664 Inputs: CurrentCount - 0 if you are not maintaining a percentage display, or if you 00665 simply don't want to update the percentage on this particular call. 00666 Otherwise, a value between 0 and the FinalCount value passed to Start(). 00667 This value is used to calculate a percentage value to display. 00668 00669 FreqEscChecks = if TRUE, the escape key is checked once, guaranteed 00670 if FALSE, the escape key will only be checked if the percentage displayed increases 00671 00672 Returns: TRUE if everything is OK 00673 FALSE if the user has aborted the job by pressing 'escape' 00674 00675 Purpose: Updates progress indications. This serves 3 purposes: 00676 a) To show the progress indications if a delayed-start was specified in 00677 your call to Start(). This is the default action. 00678 b) To check for the user pressing escape to abort the job 00679 c) To change the displayed percentage on the progress bar and animated cursor 00680 00681 Notes: This function is very efficient so it is perfectly safe to call it often. 00682 00683 In fact, if it is not called often, 00684 a) Delayed start won't work, and you may not get an hourglass at all! 00685 b) The user won't be able to abort part way through a job 00686 00687 WHEN IN DOUBT, CALL THIS ROUTINE. IT IS BETTER TO CALL IT TOO OFTEN 00688 THAN TO NOT CALL IT OFTEN ENOUGH. 00689 00690 It is safe to call this function at ANY time, even if no progress indication 00691 is active, so if your function can take more than 1/3rd of a second to 00692 execute, it really should call Update occasionally just to ensure smooth 00693 operation. 00694 00695 You need not check the return value on every call. Once the user has pressed 00696 escape, this function will always return FALSE until someone takes note. 00697 Thus, routines which cannot cope with abortion can possibly leave abortion 00698 to their callers. 00699 00700 The percentage is only updated if it has become greater than the currently 00701 displayed percentage value. 00702 00703 The FreqEscChecks parameter (markn) - this param will control the frequency 00704 with which the escape key is checked when this function is called. 00705 If it is TRUE, it is checked once, guaranteed. 00706 If it is FALSE, it is only checked if the percentage displayed increases. 00707 00708 You should only call with FreqEscChecks = FALSE if you can detect a performance hit due 00709 to too many escape key checks. 00710 00711 NOTE: If you call with FreqEscChecks = FALSE, and you never display a percentage, the user 00712 will not be able to abort the op part way through. 00713 00714 00715 00716 SeeAlso: Progress::Start; Progress::Update; Progress::Stop; Progress::Smash 00717 00718 ********************************************************************************************/ 00719 00720 BOOL Progress::Update(INT32 CurrentCount,BOOL FreqEscChecks) 00721 { 00722 #if DEBUG_OUTPUT 00723 TRACE( _T("Progress::Update ActiveDisplays = %d CurrentCount = %d FinalCount=%d\n"),ActiveDisplays,CurrentCount, FinalCount); 00724 #endif 00725 00726 // --- If no active progress display is on, then return immediately 00727 if (ActiveDisplays <= 0) 00728 { 00729 ActiveDisplays = 0; 00730 return(TRUE); 00731 } 00732 00733 // --- If the job has been cancelled, then return FALSE to indicate user intervention 00734 if (JobCancelled) 00735 return(FALSE); 00736 00737 // If FreqEscChecks is TRUE, ensure we check the escape key, no matter what percentage is displayed 00738 if (FreqEscChecks) 00739 { 00740 // Has the user cancelled the job via the Escape key? 00741 if (KeyPress::IsEscapePressed()) 00742 { 00743 JobCancelled = TRUE; 00744 Beep(); 00745 return(FALSE); 00746 } 00747 } 00748 00749 // --- If the startup delay (if any) has not expired, we do nothing just yet 00750 if (JobDelayed && !StartTime.Elapsed(STARTDELAY)) 00751 return(TRUE); 00752 00753 // --- Check that the parameter passed in was vaguely sensible 00754 ERROR3IF(CurrentCount < 0, "Progress::Update called with out of range parameter"); 00755 00756 // --- If we haven't yet shown the hourglass cursor, do it now 00757 if (!HourglassShown) 00758 { 00759 #if DELAY_THREAD 00760 ThreadAlive = FALSE; // Stop the delay thread (if any) because it's not needed now 00761 #endif 00762 BeginBusyCursor(); 00763 HourglassShown = TRUE; 00764 00765 } 00766 00767 // --- Calculate the new percentage to display. This is limited to lie between 00768 // the last displayed percentage and 99 inclusive. 00769 INT32 NewPercent = CurrentPercent; 00770 if (FinalCount > 0) 00771 { 00772 NewPercent = (100 * CurrentCount) / FinalCount; 00773 00774 if (NewPercent > 99) 00775 NewPercent = 99; 00776 00777 if (NewPercent < CurrentPercent) 00778 NewPercent = CurrentPercent; 00779 } 00780 00781 // --- Now, update the percentage display if it has changed, or if it is a while since 00782 // our last update. NOTE: Updates occur periodically for (a) checking the escape key state 00783 // occasionally, so we can be called often without a huge performance hit, and (b) to 00784 // update frames of our animated hourglass pointer. 00785 if (NewPercent > CurrentPercent) 00786 { 00787 // If FreqEscChecks is FALSE, we only check the escape key when the percentage increases 00788 if (!FreqEscChecks) 00789 { 00790 // Has the user cancelled the job via the Escape key? 00791 if (KeyPress::IsEscapePressed()) 00792 { 00793 JobCancelled = TRUE; 00794 Beep(); 00795 return(FALSE); 00796 } 00797 } 00798 00799 // (Create if necessary) and update the progress bar, if it is needed 00800 if (DisplayBar && FinalCount > 0) 00801 { 00802 if (StatusLine::Get()) 00803 { 00804 StatusLine::Get()->ShowProgress(TRUE, &JobDescription); 00805 StatusLine::Get()->SetPercent(NewPercent); 00806 } 00807 } 00808 00809 // Remember the last displayed percentage 00810 CurrentPercent = NewPercent; 00811 } 00812 00813 return(TRUE); 00814 } 00815 00816 00817 00818 /******************************************************************************************** 00819 00820 > void Progress::Stop(void) 00821 00822 Author: Jason_Williams (Xara Group Ltd) <camelotdev@xara.com> 00823 Date: 10/11/95 00824 00825 Purpose: Stops a progress indication started with a call to Start() 00826 The hourglass and progress bar etc will only be removed once all calls 00827 to Start() have been countermanded by calls to Stop(). 00828 00829 Notes: Be careful to call Stop() exactly the same number of times as Start() through 00830 each possible execution path! 00831 00832 It is recommended that you do not call Start() and Stop() directly, but 00833 instead construct a local instance of the Progress class in your function. 00834 This will automatically start and stop the hourglass for you, and also 00835 ensures that Start() and Stop() calls are correctly nested/balanced. 00836 00837 SeeAlso: Progress::Start; Progress::Update; Progress::Stop; Progress::Smash 00838 00839 ********************************************************************************************/ 00840 00841 void Progress::Stop(void) 00842 { 00843 #if DEBUG_OUTPUT 00844 TRACE( _T("Progress::Stop ActiveDisplays = %d \n"),ActiveDisplays); 00845 #endif 00846 00847 // Decrement the usage count. The progress display remains until the count returns to zero 00848 ActiveDisplays--; 00849 00850 if (ActiveDisplays <0) 00851 { 00852 // This should never happen but does as Op::End() calls EndSlowJob despite 00853 // BeginSlowJob() not being called first. We return immediately because Progress::Smash() is 00854 // expensive and not a great thing to do on every Op. 00855 ActiveDisplays = 0; 00856 return; 00857 } 00858 00859 // Return if there are active displays left 00860 if (ActiveDisplays > 0) 00861 return; 00862 00863 // We have zero active displays, and before the call, had a positive number, so smash the 00864 // hourglass and delete the progress bar 00865 00866 if (StatusLine::Get() && StatusLine::Get()->IsProgressShown() && CurrentPercent < 97) 00867 { 00868 // We are showing a progress bar, but have not shown "completion" of the job (99%) 00869 // Briefly jump to 99% on the progress bar so the user can't see how bad our estimate 00870 // of when we'd finish really was. 00871 00872 StatusLine::Get()->SetPercent(99); 00873 00874 ::wxMilliSleep(150); /* wait */ 00875 } 00876 00877 // Make sure everything is reset properly 00878 Progress::Smash(TRUE); 00879 } 00880 00881 00882 00883 /******************************************************************************************** 00884 00885 > void Progress::Smash(BOOL ForceSmash = FALSE) 00886 00887 Author: Jason_Williams (Xara Group Ltd) <camelotdev@xara.com> 00888 Date: 10/11/95 00889 00890 Inputs: ForceSmash - Pass in FALSE for this parameter - for internal use only 00891 [Forces the hourglass to be smashed even if we believe it is not actually 00892 running at present. Used by Progress::Stop to kill its displays when its 00893 usage count is decremented to zero. Calling with TRUE is a lot less efficient 00894 than with FALSE when no jobs are currently active] 00895 00896 Purpose: Smashes the hourglass and other progress displays. This kills all active 00897 progress indications immediately, as if Progress::Stop() had been called enough 00898 times to cancel all active displays. 00899 00900 Notes: Returns very quickly if no jobs are running, so can be called often 00901 without significant loss of efficiency. 00902 00903 This is called on each idle event to ensure that spurious hourglasses 00904 are removed. 00905 00906 SeeAlso: Progress::Start; Progress::Update; Progress::Stop; Progress::Smash 00907 00908 ********************************************************************************************/ 00909 00910 void Progress::Smash(BOOL ForceSmash) 00911 { 00912 if (ActiveDisplays > 0 || ForceSmash) 00913 { 00914 #if DELAY_THREAD 00915 ThreadAlive = FALSE; // Stop the delay thread (if any) 00916 #endif 00917 00918 // --- Tell the node subsystem that we're turning off the hourglass 00919 Node::StopHourglass(); 00920 00921 // --- Blank the status line text 00922 String_256 Blank(""); 00923 GetApplication()->UpdateStatusBarText(&Blank); 00924 00925 // --- Remove any active progress bar 00926 if (StatusLine::Get()) 00927 { 00928 StatusLine::Get()->ShowProgress(FALSE); 00929 } 00930 00931 // --- And reset all variables to suitable defaults 00932 ActiveDisplays = 0; 00933 JobCancelled = FALSE; 00934 00935 JobDelayed = TRUE; 00936 FinalCount = 0; 00937 CurrentPercent = 0; 00938 HourglassShown = FALSE; 00939 00940 JobDescription.MakeMsg(_R(IDS_BUSYMSG)); 00941 } 00942 00943 // --- And ensure no busy cursors are left active 00944 SmashBusyCursor(); 00945 } 00946 00947 00948 00949 /******************************************************************************************** 00950 00951 > static BOOL Progress::Init(void) 00952 00953 Author: Jason_Williams (Xara Group Ltd) <camelotdev@xara.com> 00954 Date: 10/11/95 00955 00956 Returns: TRUE for success, FALSE for failure 00957 00958 Purpose: Initialise the progress display system. Call once on startup 00959 (see main3.cpp) 00960 00961 SeeAlso: Progress::DeInit 00962 00963 ********************************************************************************************/ 00964 00965 BOOL Progress::Init(void) 00966 { 00967 Camelot.DeclareSection(TEXT("Displays"), 8); 00968 Camelot.DeclarePref(TEXT("Displays"), TEXT("ProgressBar"), &DisplayBar); 00969 00970 return(TRUE); // We successfully initialised 00971 } 00972 00973 00974 00975 /******************************************************************************************** 00976 00977 > static BOOL Progress::DeInit(void) 00978 00979 Author: Jason_Williams (Xara Group Ltd) <camelotdev@xara.com> 00980 Date: 10/11/95 00981 00982 Returns: TRUE for success, FALSE for failure 00983 00984 Purpose: De-initialise the progress display system. Call once on shutdown 00985 (see main.cpp) 00986 00987 SeeAlso: Progress::Init 00988 00989 ********************************************************************************************/ 00990 00991 BOOL Progress::Deinit(void) 00992 { 00993 return(TRUE); 00994 } 00995 00996 /******************************************************************************************** 00997 00998 > static void Progress::SetFinalCount(INT32 n); 00999 01000 Author: Mark_Neves (Xara Group Ltd) <camelotdev@xara.com> 01001 Date: 18/1/96 01002 Inputs: n = the final count value 01003 Returns: - 01004 Purpose: Allows you to set this value after construction of a Progress object. 01005 You should call this value before you make any calls to Update(). 01006 01007 If there are already other Progress objects created, this call does nothing. 01008 01009 SeeAlso: Progress::Progress() 01010 01011 ********************************************************************************************/ 01012 01013 void Progress::SetFinalCount(INT32 n) 01014 { 01015 // if (ActiveDisplays <= 1) 01016 // { 01017 // --- We're starting a brand new progress indication - so set it up 01018 FinalCount = n; 01019 // } 01020 } 01021 01022 /******************************************************************************************** 01023 01024 > INT32 Progress::GetCount(INT32 n=0) 01025 01026 Author: Mark_Neves (Xara Group Ltd) <camelotdev@xara.com> 01027 Date: 18/1/96 01028 Inputs: n = the increment value 01029 Returns: Current count+n 01030 Purpose: Gets the current progress count, and add 'n' to it. 01031 'n' is added before the current count is returned 01032 01033 This function doubles up as an incrementing function for the progress count. 01034 01035 If the final count var (as supplied to the constructor or SetFinalCount()) is 01036 less that 1, the counter is not incremented AND 0 is always returned. 01037 01038 SeeAlso: Progress::SetFinalCount(), Progress::ResetCount() 01039 01040 ********************************************************************************************/ 01041 01042 INT32 Progress::GetCount(INT32 n) 01043 { 01044 if (FinalCount > 0) 01045 { 01046 ProgressCount += n; 01047 return ProgressCount; 01048 } 01049 else 01050 return 0; 01051 } 01052 01053 /******************************************************************************************** 01054 01055 > void Progress::ResetCount(INT32 n=0) 01056 01057 Author: Mark_Neves (Xara Group Ltd) <camelotdev@xara.com> 01058 Date: 18/1/96 01059 Inputs: - 01060 Returns: - 01061 Purpose: Resets the progress count to n 01062 01063 SeeAlso: Progress::Progress(), Progress::GetCount() 01064 01065 ********************************************************************************************/ 01066 01067 void Progress::ResetCount(INT32 n) 01068 { 01069 ProgressCount = n; 01070 } 01071 01072 #else 01073 01074 // Stubs for use in Ralph, who has no progress system... 01075 BOOL BeginSlowJob(INT32 finalcount, BOOL delay, String_64 *Description) 01076 { 01077 // Ralph 01078 return TRUE; 01079 } 01080 BOOL ContinueSlowJob(INT32 upto) 01081 { 01082 // test for abort 01083 return Progress::Update(0,0); 01084 } 01085 void EndSlowJob(void) 01086 { 01087 // Ralph 01088 } 01089 void SmashSlowJob(void) 01090 { 01091 // Ralph 01092 return; 01093 } 01094 void Progress::ThreadCallback(void) 01095 { 01096 // Ralph 01097 } 01098 Progress::Progress() 01099 { 01100 // Ralph 01101 } 01102 Progress::Progress(StringBase *Description, INT32 FinalCount,BOOL Delay, BOOL bEnable) 01103 { 01104 // Ralph 01105 } 01106 Progress::Progress(UINT32 DescriptionID, INT32 FinalCount,BOOL Delay, BOOL bEnable) 01107 { 01108 // Ralph 01109 } 01110 Progress::~Progress() 01111 { 01112 // Ralph 01113 } 01114 void Progress::Start(BOOL Delay, UINT32 DescriptionID, INT32 IntendedFinalCount) 01115 { 01116 // Ralph 01117 } 01118 void Progress::Start(BOOL Delay, StringBase *Description, INT32 IntendedFinalCount) 01119 { 01120 // Ralph 01121 } 01122 BOOL Progress::Update(INT32 CurrentCount,BOOL FreqEscChecks) 01123 { 01124 // test for a fake escape condition 01125 return !AbortJob; 01126 } 01127 void Progress::Stop(void) 01128 { 01129 // Ralph 01130 } 01131 void Progress::Smash(BOOL ForceSmash) 01132 { 01133 // Ralph 01134 } 01135 BOOL Progress::Init(void) 01136 { 01137 // Ralph 01138 return TRUE; 01139 } 01140 BOOL Progress::Deinit(void) 01141 { 01142 // Ralph 01143 return TRUE; 01144 } 01145 void Progress::SetFinalCount(INT32 n) 01146 { 01147 // Ralph 01148 } 01149 INT32 Progress::GetCount(INT32 n) 01150 { 01151 // Ralph 01152 return 0; 01153 } 01154 void Progress::ResetCount(INT32 n) 01155 { 01156 // Ralph 01157 } 01158 01159 #endif