ctrlhelp.cpp

Go to the documentation of this file.
00001 // $Id: ctrlhelp.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 // Code to modify the behaviour of controls on Camelot bars so that they respond
00100 // to help messages and can be dragged about etc.
00101 
00102 
00103 #include "camtypes.h"
00104 
00105 #include "ctrlhelp.h"
00106 
00107 //#include "app.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00108 //#include "fixmem.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00109 //#include "errors.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00110 //#include "ensure.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00111 //#include "opdesc.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00112 #include "camframe.h"
00113 #include "bblwnd.h"
00114 //#include "ops.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00115 //#include "tool.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00116 #include "dlgmgr.h"
00117 //#include "fonts.h"
00118 #include "keypress.h"
00119 //#include "childbar.h"
00120 #include "statline.h"
00121 
00122 //#include "textres.h"  // required so we know what an _R(IDC_FONT_COMBO) is.
00123 #include "textinfo.h"   // required so we know what a TextInfoBarOp is.
00124 
00125 //#include "bitbutn.h"
00126 
00127 #define STATE_TRACE 0
00128 
00129 
00130 wxWindow* EMPTY_SLOT = NULL;
00131 extern wxWindow* LastPointerInButton; // for new flat look - last button plinthed
00132                                  // refers to the global variable defined in bitbutn.cpp
00133 
00134 /********************************************************************************************
00135 
00136 >   class BarTable : public SimpleCCObject
00137 
00138     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00139     Created:    27/04/94
00140     Purpose:    Provide an array of window handles for all the existing Camelot bars.
00141                 This enables us to get bubble help to behave correctly, i.e. when the 
00142                 cursor is over the bar, the bubble help goes away, but the delay timer
00143                 for bubble help is not restarted.
00144     SeeAlso:    ControlHelper
00145 
00146 ********************************************************************************************/
00147 
00148 class BarTable : public SimpleCCObject
00149 {
00150 public:
00151     BarTable();
00152     ~BarTable();
00153     BOOL Init();
00154 
00155     BOOL AddBar(wxWindow*);
00156     BOOL DeleteBar(wxWindow*);
00157     BOOL ChangeBar(wxWindow*, wxWindow*);
00158 
00159     BOOL IsABar(wxWindow*);
00160 
00161 private:
00162     INT32 FindBarIndex(wxWindow*);
00163 
00164     enum
00165     {
00166         NotFound = -1,
00167         EmptySlot = 0,
00168         Granularity = 5,
00169         InitialSize = 10
00170     };
00171 
00172     wxWindow** Table;
00173     INT32 TableSize;
00174 };
00175 
00176 /********************************************************************************************
00177 
00178 >   BarTable::BarTable()
00179 
00180     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00181     Created:    26/04/94
00182     Purpose:    Construct the bar table object.
00183     SeeAlso:    BarTable::Init
00184 
00185 ********************************************************************************************/
00186 
00187 BarTable::BarTable()
00188 {
00189     // No table data yet.
00190     Table = NULL;
00191     TableSize = 0;
00192 }
00193 
00194 /********************************************************************************************
00195 
00196 >   BarTable::~BarTable()
00197 
00198     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00199     Created:    26/04/94
00200     Purpose:    Destroy a BarTable object - frees up all memory used by the table data.
00201     SeeAlso:    BarTable::Init
00202 
00203 ********************************************************************************************/
00204 
00205 BarTable::~BarTable()
00206 {
00207     // Free up the table data.
00208     CCFree((void*)Table);
00209 }
00210 
00211 /********************************************************************************************
00212 
00213 >   BOOL BarTable::Init()
00214 
00215     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00216     Created:    26/04/94
00217     Returns:    TRUE if successful;
00218                 FALSE if not.
00219     Purpose:    Allocate and initialise the table of bar entries.
00220     Errors:     Out of memory.
00221     SeeAlso:    BarTable
00222 
00223 ********************************************************************************************/
00224 
00225 BOOL BarTable::Init()
00226 {
00227     // Try to get some space for our table.
00228     Table = (wxWindow**) CCMalloc(BarTable::InitialSize * sizeof(wxWindow*));
00229     if (Table == NULL)
00230         return FALSE;
00231 
00232     // Initialise the table
00233     for (INT32 i = 0; i < BarTable::InitialSize; i++)
00234     {
00235         // No control registered in this entry yet
00236         Table[i] = EMPTY_SLOT;
00237     }
00238 
00239     // Update the table size
00240     TableSize = BarTable::InitialSize;
00241 
00242     // Success!
00243     return TRUE;
00244 }
00245 
00246 /********************************************************************************************
00247 
00248 >   BOOL BarTable::AddBar(wxWindow* Window)
00249 
00250     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00251     Created:    26/04/94
00252     Inputs:     Window - the handle of the bar.
00253     Returns:    TRUE if the bar was added to the table successully;
00254                 FALSE if not.
00255     Purpose:    Add a barhandle to the table, using the data passed in.  The table
00256                 will expand to accomodate the new bar if necessary and if possible.
00257     Errors:     Out of memory, Bar already exists in the table (debug builds only).
00258     SeeAlso:    BarTable::DeleteBar; BarTable::FindBar
00259 
00260 ********************************************************************************************/
00261 
00262 BOOL BarTable::AddBar(wxWindow* Window)
00263 {
00264     // Basic sanity checks.
00265     ENSURE(Window != NULL, "NULL window handle in BarTable::AddBar");
00266     if (Window == NULL )
00267         return FALSE;
00268     ENSURE(FindBarIndex(Window) == BarTable::NotFound, "Bar already exists in BarTable::AddBar");
00269 
00270     INT32 i = 0;
00271     while ((i < TableSize) && (Table[i] != EMPTY_SLOT))
00272         i++;
00273 
00274     if (i >= TableSize)
00275     {
00276         // No free slots - extend the table.
00277         INT32 NewTableSize = TableSize + BarTable::Granularity;
00278         wxWindow* *NewTable = (wxWindow* *) CCRealloc((void*)Table, NewTableSize * sizeof(wxWindow*));
00279         if (NewTable == NULL)
00280             return FALSE;
00281 
00282         // Table extended ok - point 'i' at the first free slot, and update table variables.
00283         i = TableSize;
00284         Table = NewTable;
00285         TableSize = NewTableSize;
00286 
00287         for (INT32 j=i+1;j<TableSize;j++)
00288             Table[j] = EMPTY_SLOT;
00289     }
00290 
00291     // If we've got this far, 'i' points at a valid free slot in the table.
00292     Table[i] = Window;
00293 
00294     // Everything worked
00295     return TRUE;
00296 }
00297 
00298 /********************************************************************************************
00299 
00300 >   BOOL BarTable::DeleteBar(wxWindow* Window)
00301 
00302     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00303     Created:    26/04/94
00304     Inputs:     Window - the handle of the control to remove.
00305     Returns:    TRUE if the control was removed ok;
00306                 FALSE if the control could not be found in the table.
00307     Purpose:    Remove the control from the table of subclassed controls.
00308     Errors:     Control's window handle was not found in the table (ENSUREs in debug builds).
00309     SeeAlso:    BarTable::AddControl; BarTable::FindControl; ControlHelper;
00310                 ControlEntry
00311 
00312 ********************************************************************************************/
00313 
00314 BOOL BarTable::DeleteBar(wxWindow* Window)
00315 {
00316     // Basic sanity checks
00317     ENSURE(Window != 0, "NULL Window handle in BarTable::DeleteBar!");
00318     if (Window == 0)
00319         return FALSE;
00320 
00321     // Search for the specified bar.
00322     INT32 i = FindBarIndex(Window);
00323 
00324     ENSURE(i != BarTable::NotFound, "Control not found in BarTable::DeleteBar");
00325 
00326     if (i == BarTable::NotFound)
00327         // Unable to find any record of the bar
00328         return FALSE;
00329 
00330     // Found the bar - delete it
00331     Table[i] = EMPTY_SLOT;
00332 
00333     // Return success
00334     return TRUE;
00335 }
00336 
00337 /********************************************************************************************
00338 
00339 >   BOOL BarTable::ChangeBar(wxWindow* Old, wxWindow* New)
00340 
00341     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00342     Created:    27/04/94
00343     Inputs:     Old - the old window handle of the bar.
00344                 New - the new window handle of the bar.
00345     Returns:    TRUE if the bar was changed successfully;
00346                 FALSE if not.
00347     Purpose:    Inform the table that a bar has been deleted and recreated with a new window
00348                 handle.
00349     Errors:     The bar could not be found.
00350     SeeAlso:    BarTable::AddBar; BarTable::DeleteBar
00351 
00352 ********************************************************************************************/
00353 
00354 BOOL BarTable::ChangeBar(wxWindow* Old, wxWindow* New)
00355 {
00356     // Basic sanity checks
00357     ENSURE((Old != NULL) && (New != NULL), "NULL Window handle in BarTable::ChangeBar!");
00358     if ((Old == NULL) || (New == NULL))
00359         return FALSE;
00360 
00361     // Search for the specified bar.
00362     INT32 i = FindBarIndex(Old);
00363 
00364     ENSURE(i != BarTable::NotFound, "Control not found in BarTable::ChangeBar");
00365 
00366     if (i == BarTable::NotFound)
00367         // Unable to find any record of the bar
00368         return FALSE;
00369 
00370     // Found the bar - update the window handle.
00371     Table[i] = New;
00372 
00373     // Return success
00374     return TRUE;
00375 }
00376 
00377 
00378 /********************************************************************************************
00379 
00380 >   BOOL BarTable::IsABar(wxWindow* Window)
00381 
00382     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00383     Created:    27/04/94
00384     Inputs:     Window - the window handle to check.
00385     Returns:    TRUE if this window handle is registered as a Camelot bar;
00386                 FALSE if not.
00387     Purpose:    Find out if a given window handle is the handle of a Camelot bar, as
00388                 recorded in this BarTable object.
00389     SeeAlso:    ControlHelper
00390 
00391 ********************************************************************************************/
00392 
00393 BOOL BarTable::IsABar(wxWindow* Window)
00394 {
00395     ENSURE(Window != NULL, "NULL Window in BarTable::IsABar");
00396     if (Window == NULL)
00397         return FALSE;
00398 
00399     return (FindBarIndex(Window) != BarTable::NotFound);
00400 }
00401 
00402 /********************************************************************************************
00403 
00404 >   INT32 BarTable::FindBarIndex(wxWindow* Window)
00405 
00406     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00407     Created:    26/04/94
00408     Inputs:     Window - the handle of the control to search for.
00409     Returns:    The index into the Table array of the control's record, or
00410                 BarTable::NotFound if the control is not registered in the table.
00411     Purpose:    Given the window handle of a control, return the offset into the table
00412                 array of its record.
00413     Errors:     Not found.
00414     SeeAlso:    BarTable::FindControl; BarTable::DeleteControl
00415 
00416 ********************************************************************************************/
00417 
00418 INT32 BarTable::FindBarIndex(wxWindow* Window)
00419 {
00420     // Scane the table for this entry
00421     INT32 i = 0;
00422     while ((i < TableSize) && (Table[i] != Window))
00423         i++;
00424 
00425     // Did we find it?
00426     if (Table[i] != Window)
00427         // No - inform caller of failure.
00428         return BarTable::NotFound;
00429 
00430     // Yes, success
00431     return i;
00432 }
00433 
00434 
00435 
00436 /*****************************************************************************
00437 >   BOOL ControlEntry::ControlStatusLineText(String_256* ptext)
00438 
00439     Author:     Ed_Cornes (Xara Group Ltd) <camelotdev@xara.com>
00440     Created:    20/11/94
00441     Returns:    TRUE if a valid string was written to the text buffer
00442     Purpose:    return the status-line text for a given ControlEntry object
00443 *****************************************************************************/
00444 
00445 BOOL ControlEntry::ControlStatusLineText(String_256* ptext)
00446 {
00447     // if valid OpDesc, load status-line text from OpDescriptor
00448     if (pOpDesc != NULL)
00449         return pOpDesc->GetText(ptext,OP_DESC_TEXT);
00450 
00451     // If there's no status-line string associated with the control then make the
00452     // string blank.
00453     if (StatusID == 0)
00454     {
00455         TRACEUSER( "JustinF", _T("ControlEntry at 0x%lX has zero status-line string ID\n"),
00456                                 (UINT32) this);
00457         ptext->Empty();
00458         return TRUE;
00459     }
00460 
00461     // else just load using string ID provided directly by caller.
00462     return ptext->Load(StatusID,ModuleID);
00463 }
00464 
00465 
00466 /********************************************************************************************
00467 
00468 >   ControlTable::ControlTable()
00469 
00470     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00471     Created:    26/04/94
00472     Purpose:    Construct the control table object.
00473     SeeAlso:    ControlTable::Init
00474 
00475 ********************************************************************************************/
00476 
00477 ControlTable::ControlTable()
00478 {
00479     // No table data yet.
00480     Table = NULL;
00481     TableSize = 0;
00482 
00483     // Initial state
00484     LastWindow = NULL;
00485     LastIndex = 0;
00486 }
00487 
00488 /********************************************************************************************
00489 
00490 >   ControlTable::~ControlTable()
00491 
00492     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00493     Created:    26/04/94
00494     Purpose:    Destroy a ControlTable object - frees up all memory used by the table data.
00495     SeeAlso:    ControlTable::Init
00496 
00497 ********************************************************************************************/
00498 
00499 ControlTable::~ControlTable()
00500 {
00501     // Free up the table data.
00502     CCFree(Table);
00503 }
00504 
00505 /********************************************************************************************
00506 
00507 >   BOOL ControlTable::Init()
00508 
00509     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00510     Created:    26/04/94
00511     Returns:    TRUE if successful;
00512                 FALSE if not.
00513     Purpose:    Allocate and initialise the table of control entries.
00514     Errors:     Out of memory.
00515     SeeAlso:    ControlTable
00516 
00517 ********************************************************************************************/
00518 
00519 BOOL ControlTable::Init()
00520 {
00521     // Try to get some space for our table.
00522     Table = (ControlEntry *) CCMalloc(ControlTable::InitialSize * sizeof(ControlEntry));
00523     if (Table == NULL)
00524         return FALSE;
00525 
00526     // Initialise the table
00527     for (INT32 i = 0; i < ControlTable::InitialSize; i++)
00528     {
00529         // No control registered in this entry yet
00530         Table[i].Window = EMPTY_SLOT;
00531     }
00532 
00533     // Update the table size
00534     TableSize = ControlTable::InitialSize;
00535 
00536     // Success!
00537     return TRUE;
00538 }
00539 
00540 /********************************************************************************************
00541 
00542 >   BOOL ControlTable::AddControl(wxWindow* Window, ControlHelpInfo *pInfo, WNDPROC WndProc)
00543 
00544     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00545     Created:    26/04/94
00546     Inputs:     Window - the handle of the control (this is used as a minimal key for the
00547                          table entries).
00548                 pInfo - information about the control to add.
00549                 WndProc - the WndProc that the control normally uses.
00550     Returns:    TRUE if the control was added to the table successully;
00551                 FALSE if not.
00552     Purpose:    Add a control record to the table, using the data passed in.  The table
00553                 will expand to accomodate the new control if necessary and if possible.
00554     Errors:     Out of memory, Control already exists in the table (debug builds only).
00555     SeeAlso:    ControlTable::DeleteControl; ControlTable::FindControl
00556 
00557 ********************************************************************************************/
00558 
00559 PORTNOTE( "dialog", "Remove function that needs WNDPROC" )
00560 #if !defined(EXCLUDE_FROM_XARALX)
00561 BOOL ControlTable::AddControl(wxWindow* Window, ControlHelpInfo* pInfo, WNDPROC WndProc)
00562 {
00563     // Basic sanity checks.
00564     ERROR3IF(pInfo == NULL, "Null ControlHelpInfo* in ControlTable::AddControl");
00565     ENSURE(Window != NULL, "NULL window handle in ControlTable::AddControl");
00566     ENSURE(WndProc != NULL, "NULL WndProc in ControlTable::AddControl");
00567 //  ERROR3IF(pInfo->StatusID == 0, "Zero string ID in ControlTable::AddControl");
00568     
00569     if ((Window == NULL ) || (WndProc == NULL))
00570         return FALSE;
00571 
00572     // Start from the end of array and search backwards (it's more efficient for large
00573     // arrays).
00574     INT32 i = TableSize - 1;
00575 
00576     while ((i >= 0) && (Table[i].Window != EMPTY_SLOT))
00577         i--;
00578 
00579     if (i < 0)
00580     {
00581         // No free slots - extend the table.
00582         INT32 NewTableSize = TableSize + ControlTable::Granularity;
00583         ControlEntry *NewTable = 
00584             (ControlEntry *) CCRealloc(Table, NewTableSize * sizeof(ControlEntry));
00585         if (NewTable == NULL)
00586             return FALSE;
00587 
00588         // Table extended ok - point 'i' at the first free slot, and update table variables.
00589         i = TableSize;
00590         Table = NewTable;
00591         TableSize = NewTableSize;
00592 
00593         for (INT32 j=i+1;j<TableSize;j++)
00594             Table[j].Window = EMPTY_SLOT;
00595     }
00596 
00597     // If we've got this far, 'i' points at a valid free slot in the table.
00598     Table[i].Window   = Window;
00599     Table[i].WndProc  = WndProc;
00600     Table[i].Parent   = pInfo->Parent;
00601     Table[i].pOpDesc  = pInfo->pOpDesc;
00602     Table[i].BubbleID = pInfo->BubbleID;
00603     Table[i].StatusID = pInfo->StatusID;
00604     Table[i].ModuleID = pInfo->ModuleID;  
00605     
00606     // If the Control being subclassed is a combo or an edit then we need to add
00607     // commit handling.
00608 
00609     String_256 ClassNameStr;  
00610     GetClassName(Window, (TCHAR*)ClassNameStr, 255);
00611     Table[i].AddCommitHandling = ( (ClassNameStr == String_8(TEXT("Edit"))) ||
00612                                    (ClassNameStr == String_8(TEXT("ComboBox"))) ||
00613 //                                 (ClassNameStr == String_64(TEXT("cc_CustomEdit"))) ||                                    
00614                                    (ClassNameStr == String_64(TEXT("cc_1dBitmapComboBoxEdit"))) ||
00615                                    (ClassNameStr == String_64(TEXT("cc_2dBitmapComboBoxEdit"))) );
00616 
00617     // Set cache back to default
00618     LastWindow = NULL;
00619     LastIndex = 0;
00620 
00621     // Everything worked
00622     return TRUE;
00623 }
00624 #endif
00625 
00626 /********************************************************************************************
00627 
00628 >   WNDPROC ControlTable::DeleteControl(wxWindow* Window, wxWindow* *RealWindow)
00629 
00630     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00631     Created:    26/04/94
00632     Inputs:     Window - the handle of the control to remove (including its child windows).
00633     Outputs:    The window handle of the entry just deleted, which for child windows will
00634                 not be the same as the 'Window' input parameter. Pass in NULL if you don't
00635                 want this window handle.
00636     Returns:    The original WNDPROC of the control that was deleted, or
00637                 NULL if the control could not be found in the table.
00638     Purpose:    Remove the control or a child of it from the table of subclassed controls, 
00639                 and return the original WndProc for that window.
00640                 This function should be called repetitively for a window handle until NULL
00641                 is returned, to ensure that all child window entries of the control are
00642                 deleted too.
00643     Errors:     ENSUREs if NULL window handle passed in.
00644     SeeAlso:    ControlTable::AddControl; ControlTable::FindControl; ControlHelper;
00645                 ControlEntry
00646 
00647 ********************************************************************************************/
00648 
00649 WNDPROC ControlTable::DeleteControl(wxWindow* Window, wxWindow* *RealWindow)
00650 {
00651     // Basic sanity checks
00652     ENSURE(Window != 0, "NULL Window handle in ControlTable::DeleteControl!");
00653     if (Window == 0)
00654         return FALSE;
00655 
00656     // Search for the specified control and its children.
00657     INT32 i = FindControlIndex(Window, FALSE);
00658 
00659     if (i == ControlTable::NotFound)
00660         // No more controls associated with this window ID...
00661         return NULL;
00662 
00663     // Found the control - delete it
00664     if (RealWindow != NULL)
00665         *RealWindow = Table[i].Window;
00666     Table[i].Window = EMPTY_SLOT;
00667     Table[i].Parent = NULL;
00668 
00669     // Return success
00670     return Table[i].WndProc;
00671 }
00672 
00673 BOOL ControlTable::ChangeControl(wxWindow* Window, ControlHelpInfo *pInfo)
00674 {
00675     INT32 i = FindControlIndex(Window);
00676 
00677     if (i == ControlTable::NotFound)
00678         return FALSE;
00679 
00680     Table[i].BubbleID = pInfo->BubbleID;
00681     Table[i].StatusID = pInfo->StatusID;
00682     Table[i].ModuleID = pInfo->ModuleID;  
00683 
00684     // Everything worked
00685     return TRUE;
00686 }
00687 
00688 /********************************************************************************************
00689 
00690 >   ControlEntry *ControlTable::FindControl(wxWindow* Window)
00691 
00692     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00693     Created:    26/04/94
00694     Inputs:     Window - the handle of the control to look for.
00695     Returns:    Pointer to the data currently registered for the control in question,
00696                 or NULL if the control could not be found.
00697     Purpose:    Given a control's window handle, return the information currently registered
00698                 for that control.  Do not free the data pointed to by the return value.
00699     SeeAlso:    ControlEntry
00700 
00701 ********************************************************************************************/
00702 
00703 ControlEntry *ControlTable::FindControl(wxWindow* Window)
00704 {
00705     INT32 i=FindControlIndex(Window);
00706 
00707     if (i==ControlTable::NotFound)
00708         return NULL;
00709 
00710     return Table + i;
00711 }
00712 
00713 
00714 /********************************************************************************************
00715 
00716 >   INT32 ControlTable::FindControlIndex(wxWindow* Window, BOOL IgnoreChildren = TRUE)
00717 
00718     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00719     Created:    26/04/94
00720     Inputs:     Window         - the handle of the control to search for.
00721                 IgnoreChildren - if TRUE, then only return the entry for the control
00722                                  itself; if FALSE then return entries for the control or
00723                                  any children of it.
00724     Returns:    The index into the Table array of the control's record, or
00725                 ControlTable::NotFound if the control is not registered in the table.
00726     Purpose:    Given the window handle of a control, return the offset into the table
00727                 array of its record, or optionally of the control's child windows.
00728     Errors:     Not found.
00729     SeeAlso:    ControlTable::FindControl; ControlTable::DeleteControl
00730 
00731 ********************************************************************************************/
00732 
00733 INT32 ControlTable::FindControlIndex(wxWindow* Window, BOOL IgnoreChildren)
00734 {
00735     // Scan the table for this entry...
00736     INT32 i;
00737 
00738     // Is this the same as the last window handle we looked for?
00739     // If so, start from the last slot we looked at.
00740     if (LastWindow == Window)
00741         i = LastIndex;
00742     else 
00743         i = 0;
00744 
00745     while ((i < TableSize) && (Table[i].Window != Window) &&
00746            (IgnoreChildren || (Table[i].Parent != Window)))
00747         i++;
00748 
00749     // Did we find it?
00750     if (i >= TableSize)
00751         // No - inform caller of failure.
00752         return ControlTable::NotFound;
00753 
00754     // Yes, success
00755     LastIndex = i;
00756     LastWindow = Window;
00757 
00758     return i;
00759 }
00760 
00761 
00763 //
00764 //                      ControlHelper Member Functions + Data
00765 //
00767 
00768 // Pointer to the table of sublassed controls we're managing at present.
00769 ControlTable *ControlHelper::Controls = NULL;
00770 
00771 // Pointer to the list of bars we currently know about.
00772 BarTable *ControlHelper::Bars = NULL;
00773 
00774 // Window handle of the last control that the cursor was over.
00775 wxWindow* ControlHelper::LastControl = NULL;
00776 
00777 // The last position of the cursor (used to defer bubble help updates).
00778 POINT ControlHelper::LastPos = {-1, -1};
00779 
00780 // Pointer to the bubble help window.
00781 BubbleHelpWnd *ControlHelper::BubbleWnd = NULL;
00782 
00783 // Timer and flag to control small delay between consecutive bubble help display.
00784 MonotonicTime ControlHelper::PendingTimer;
00785 
00786 // State of the bubble help system
00787 ControlHelper::BubbleState ControlHelper::BubbleHelpState = STATE_DEAD;
00788 
00789 // TRUE if one of our controls has focus, in which case bubble help should not appear.
00790 BOOL ControlHelper::ControlHasFocus = FALSE;
00791 
00792 // Variables for ad-hoc bubble help
00793 wxWindow* ControlHelper::AdHocWindow = NULL;
00794 UINT32 ControlHelper::AdHocControl = 0;
00795 BOOL ControlHelper::AdHocControlIsDifferent = TRUE;
00796 void *ControlHelper::AdHocReference = NULL;
00797 ControlHelper::BubbleHelpCallback ControlHelper::AdHocCallback = NULL;
00798 
00799 
00800 // Number of non-kernel modal dialogs open.
00801 INT32 ControlHelper::ModalDialogs = 0;
00802 
00803 
00804 /********************************************************************************************
00805 
00806 >   BOOL ControlHelper::Init()
00807 
00808     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00809     Created:    26/04/94
00810     Returns:    TRUE if initialised ok;
00811                 FALSE if not.
00812     Purpose:    Called to initialise the control helper system.
00813 
00814 ********************************************************************************************/
00815 
00816 BOOL ControlHelper::Init()
00817 {
00818     // Allocate and initialise a BarTable object.
00819     Bars = new BarTable;
00820     if ((Bars == NULL) || !Bars->Init())
00821         return FALSE;
00822 
00823     // Allocate and intialise ourselves a ControlTable object
00824     Controls = new ControlTable;
00825     if ((Controls == NULL) || !Controls->Init())
00826         return FALSE;
00827 
00828     // Initialise the bubble help window system
00829     return BubbleHelpWnd::Init();
00830 }
00831 
00832 /********************************************************************************************
00833 
00834 >   void ControlHelper::DeInit()
00835 
00836     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00837     Created:    26/04/94
00838     Purpose:    Deinitialise the control helper system.
00839 
00840 ********************************************************************************************/
00841 
00842 void ControlHelper::DeInit()
00843 {
00844     // Deallocate our control table and bar table objects.
00845     delete Bars;
00846     Bars = NULL;
00847     delete Controls;
00848     Controls = NULL;
00849     BubbleHelpWnd::DeInit();
00850 }
00851 
00852 /********************************************************************************************
00853 
00854 >   BOOL ControlHelper::NotifyBarCreated(wxWindow* Window)
00855 
00856     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00857     Created:    27/04/94
00858     Inputs:     Window - the handle of the bar that has just been created.
00859     Returns:    TRUE if the bar was added to ControlHelper's records successfully;
00860                 FALSE if not.
00861     Purpose:    Inform the ControlHelper class that a bar has been created with the
00862                 specified window handle.
00863     Errors:     Out of memory.
00864     SeeAlso:    ControlHelper::NotifyBarDeleted; ControlHelper::NotifyBarChanged
00865 
00866 ********************************************************************************************/
00867 
00868 BOOL ControlHelper::NotifyBarCreated(wxWindow* Window)
00869 {
00870     // Sanity checks
00871     ENSURE(Bars != NULL, "ControlHelper has not been initialised successfully");
00872     ENSURE(Window != NULL, "NULL Window handle in ControlHelper::NotifyBarCreated");
00873 
00874     // Just add the bar to our list.
00875     return Bars->AddBar(Window);
00876 }
00877 
00878 /********************************************************************************************
00879 
00880 >   BOOL ControlHelper::NotifyBarDeleted(wxWindow* Window)
00881 
00882     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00883     Created:    27/04/94
00884     Inputs:     Window - handle of the bar that has just been deleted.
00885     Returns:    TRUE if the bar handle was found;
00886                 FALSE if not.
00887     Purpose:    Notify the ControlHelper class that a bar has been deleted.
00888     SeeAlso:    ControlHelper::NotifyBarChanged; ControlHelper::NotifyBarCreated
00889 
00890 ********************************************************************************************/
00891 
00892 BOOL ControlHelper::NotifyBarDeleted(wxWindow* Window)
00893 {
00894     // Sanity checks
00895     ENSURE(Bars != NULL, "ControlHelper has not been initialised successfully");
00896     ENSURE(Window != NULL, "NULL Window handle in ControlHelper::NotifyBarDeleted");
00897 
00898     // Just remove the bar from our list.
00899     return Bars->DeleteBar(Window);
00900 }
00901 
00902 /********************************************************************************************
00903 
00904 >   BOOL ControlHelper::NotifyBarChanged(wxWindow* Old, wxWindow* New)
00905 
00906     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00907     Created:    27/04/94
00908     Inputs:     Old - the old window handle of the bar.
00909                 New - the new window handle of the bar.
00910     Returns:    TRUE if the bar's window handle was updated successfully;
00911                 FALSE if not.
00912     Purpose:    Notify the ControlHelper class that a bar's window handle has been changed.
00913     Errors:     Bar not found.
00914     SeeAlso:    ControlHelper::NotifyBarCreated; ControlHelper::NotifyBarDeleted
00915 
00916 ********************************************************************************************/
00917 
00918 BOOL ControlHelper::NotifyBarChanged(wxWindow* Old, wxWindow* New)
00919 {
00920     // Sanity checks
00921     ENSURE(Bars != NULL, "ControlHelper has not been initialised successfully");
00922     ENSURE((Old != NULL) && (New != NULL), 
00923            "NULL Window handle in ControlHelper::NotifyBarChanged");
00924 
00925     // Just update the bar in our list.
00926     return Bars->ChangeBar(Old, New);
00927 }
00928 
00929 
00930 /********************************************************************************************
00931 
00932 >   BOOL ControlHelper::AddControl(wxWindow* Window, ControlHelpInfo *pInfo)
00933 
00934     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00935     Created:    10/05/94
00936     Inputs:     Window - The window to add an entries for.
00937                 pInfo - information about the control being added.
00938     Returns:    TRUE if the window and its descendants were processed ok;
00939                 FALSE if not.
00940     Purpose:    Add entries for a window and all its descendant windows to our list of
00941                 subclassed controls.
00942                 This is a 'helper' function for ControlHelper::NotifyControlCreated.
00943     SeeAlso:    ControlTable::AddControl; ControlHelper::NotifyControlCreated
00944 
00945 ********************************************************************************************/
00946 
00947 BOOL ControlHelper::AddControl(wxWindow* Window, ControlHelpInfo *pInfo)
00948 {
00949     // Firstly, try to add this control
00950     WNDPROC WndProc = (WNDPROC) ::GetWindowLong(Window, GWL_WNDPROC);
00951     ENSURE(WndProc != 0, "NULL WndProc in ControlHelper::AddControl");
00952 
00953     if (!(Controls->AddControl(Window, pInfo, WndProc)))
00954         goto Failure;
00955 
00956     // Subclass the control.
00957     ::SetWindowLong(Window, GWL_WNDPROC, (INT32) MyWndProc);
00958 
00959     // Now try to add/subclass all the children...
00960     wxWindow* Child;
00961     Child = ::GetWindow(Window, GW_CHILD);
00962 
00963     while (Child != NULL)
00964     {
00965         // Recurse for this child window
00966         if (!(AddControl(Child, pInfo)))
00967             goto Failure;
00968 
00969         // Get the next child window, if any.
00970         Child = ::GetWindow(Child, GW_wxWindow*NEXT);
00971     }
00972 
00973     // Success!
00974     return TRUE;
00975 
00976 Failure:
00977     // Could not add all the controls for some reason - remove any we might have added
00978     // so far, but only if this call is for the parent control (cos we recurse in this
00979     // function, and we only want to do this once).
00980     if (Window == pInfo->Parent)
00981     {
00982         wxWindow* RealWindow;
00983 
00984         while ((WndProc = Controls->DeleteControl(Window, &RealWindow)) != NULL)
00985         {
00986             // De-subclass the control.
00987             WNDPROC TheWndProc = (WNDPROC) ::GetWindowLong(Window, GWL_WNDPROC);
00988             if (TheWndProc = MyWndProc)
00989                 // It really is one we've subclassed, so 'de-subclass' it!
00990                 ::SetWindowLong(Window, GWL_WNDPROC, (INT32) WndProc);
00991         }
00992     }
00993 
00994     // Failed
00995     return FALSE;
00996 }
00997 
00998 /********************************************************************************************
00999 
01000 >   BOOL ControlHelper::NotifyControlCreated(wxWindow* Window, ControlHelpInfo *pInfo)
01001 
01002     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
01003     Created:    26/04/94
01004     Inputs:     Window - the handle of the control that has been created.
01005                 pInfo - information about the control neing created
01006     Returns:    TRUE if the control was added to the ControlHelper's table ok;
01007                 FALSE if not.
01008     Purpose:    Inform the Control helper system that a control has just been created.
01009     Errors:     Out of memory; NULL window handle
01010     SeeAlso:    ControlHelper::NotifyControlDeleted; ControlHelper::NotifyControlChanged;
01011                 ControlHelper::AddControl; ControInfo
01012 
01013 ********************************************************************************************/
01014 
01015 BOOL ControlHelper::NotifyControlCreated(wxWindow* Window, ControlHelpInfo *pInfo)
01016 {
01017     // Sanity checks
01018     ENSURE(Controls != NULL, "ControlHelper has not been initialised successfully");
01019     ENSURE(Window != 0, "NULL Window handle in ControlHelper::NotifyControlCreated");
01020     ENSURE(pInfo != 0, "NULL ControlHelpInfo pointer in ControlHelper::NotifyControlCreated");
01021     if ((Controls == NULL) || (Window == 0) || (pInfo == NULL))
01022         return FALSE;
01023 
01024     // Fill in the parent field
01025     pInfo->Parent = Window;
01026 
01027     // Add the control to our list of sublassed controls.
01028     if (!AddControl(Window, pInfo))
01029         // Give up!
01030         return FALSE;
01031 
01032     // Success!
01033     return TRUE;
01034 }
01035 
01036 BOOL ControlHelper::NotifyControlCreated(wxWindow* Window, OpDescriptor *pOpDesc)
01037 {
01038     ControlHelpInfo Info;
01039     Info.pOpDesc = pOpDesc;
01040     Info.BubbleID = 0;
01041     Info.StatusID = 0;
01042     Info.ModuleID = 0;
01043 
01044     return NotifyControlCreated(Window, &Info);
01045 }
01046 
01047 /********************************************************************************************
01048 
01049 >   BOOL ControlHelper::NotifyControlChanged(wxWindow* Window, ControlHelpInfo* pInfo)
01050 
01051     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
01052     Created:    21/04/95
01053     Inputs:     Window - the handle of the control that is to be changed.
01054                 pInfo - information about the control
01055     Returns:    TRUE if the control was changed in the table ok;
01056                 FALSE if not.
01057     Purpose:    Allows the Bubble and Status ID's of a control to be changed.
01058     Errors:     Out of memory; NULL window handle
01059     SeeAlso:    ControlHelper::NotifyControlDeleted; ControlHelper::NotifyControlChanged;
01060                 ControlHelper::AddControl; ControInfo
01061 
01062 ********************************************************************************************/
01063 
01064 BOOL ControlHelper::NotifyControlChanged(wxWindow* Window, ControlHelpInfo* pInfo)
01065 {
01066     if (Controls == NULL)
01067         return FALSE;
01068     
01069     if (!Controls->ChangeControl(Window, pInfo))
01070         return FALSE;
01071 
01072     return TRUE;
01073 }
01074 
01075 /********************************************************************************************
01076 
01077 >   BOOL ControlHelper::NotifyControlDeleted(wxWindow* Window)
01078 
01079     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
01080     Created:    26/04/94
01081     Inputs:     Window - handle of the window that has been deleted.
01082     Returns:    TRUE if the control was deleted from the ControlHelper's table ok;
01083                 FALSE if not.
01084     Purpose:    Notify the control helper system that a control has just been deleted.
01085     Errors:     ControlHelper has not been initialised; Window handle is 0.
01086     SeeAlso:    ControlHelper::NotifyControlCreated; ControlHelper::NotifyControlChanged
01087 
01088 ********************************************************************************************/
01089 
01090 BOOL ControlHelper::NotifyControlDeleted(wxWindow* Window)
01091 {
01092     ENSURE(Controls != NULL, "ControlHelper has not been initialised successfully");
01093     ENSURE(Window != NULL, "NULL Window handle in ControlHelper::NotifyControlDeleted");
01094     if ((Controls == NULL) || (Window == NULL))
01095         return FALSE;
01096 
01097     // If deleting the currently active bubble help control, then kill bubble help.
01098     if ((Window == LastControl) && (BubbleHelpState != STATE_DEAD))
01099         SetState(STATE_DEAD);
01100 
01101     wxWindow* RealWindow;
01102     WNDPROC OldWndProc;
01103 
01104     while ((OldWndProc = Controls->DeleteControl(Window, &RealWindow)) != NULL)
01105     {
01106         // De-subclass the control.
01107         WNDPROC CurrentWndProc = (WNDPROC) ::GetWindowLong(RealWindow, GWL_WNDPROC);
01108 
01109         if (CurrentWndProc == MyWndProc)
01110             // It really is one we've subclassed, so 'de-subclass' it!
01111             ::SetWindowLong(RealWindow, GWL_WNDPROC, (INT32) OldWndProc);
01112     }
01113 
01114     // If we got this far it's successful
01115     return TRUE;
01116 }
01117 
01118 
01119 /*****************************************************************************
01120 >   static BOOL ControlHelper::GetStatusLineText(String_256* ptext, wxWindow* window)
01121 
01122     Author:     Ed_Cornes (Xara Group Ltd) <camelotdev@xara.com>
01123     Created:    20/11/94
01124     Returns:    TRUE if returning valid text
01125     Purpose:    Get status-line text for a control with given WHND
01126 *****************************************************************************/
01127 
01128 BOOL ControlHelper::GetStatusLineText(String_256* ptext, wxWindow* window)
01129 {
01130     if (window==NULL)
01131         return FALSE;
01132 
01133     ControlEntry* pEntry=Controls->FindControl(window);
01134     if (pEntry==NULL)
01135         return FALSE;
01136     String_256 strCaption;
01137     GetWindowText(window, strCaption, 255);
01138     if (strCaption == String_256("Download"))
01139     {
01140         *ptext = "Download selected resource to the local hard disk";
01141         return TRUE;
01142     }
01143     else return pEntry->ControlStatusLineText(ptext);
01144 }
01145 
01146 
01147 /********************************************************************************************
01148 
01149 >   LRESULT ControlHelper::MyWndProc(wxWindow* Window, UINT32 Msg, WPARAM wParam, LPARAM lParam)
01150 
01151     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
01152     Created:    26/04/94
01153     Inputs:     The usual - see SDK documentation for "WindowProc".
01154     Returns:    As for SDK WindowProc.
01155     Purpose:    Callback function for special handling of controls on Camelot bars.
01156                 At present it does nothing - just chains to the controls usual WndProc.
01157     Errors:     
01158     SeeAlso:    ControlHelper
01159 
01160 ********************************************************************************************/
01161 
01162 LRESULT ControlHelper::MyWndProc(wxWindow* Window, UINT32 Msg, WPARAM wParam, LPARAM lParam)
01163 {
01164     ENSURE(Window != 0, "NULL Window handle in ControlHelper::MyWndProc");
01165 
01166     // Find the WindowProc for this control.
01167 
01168     ControlEntry *pEntry = Controls->FindControl(Window);
01169 
01170     ENSURE(pEntry != NULL, "Could not find subclassed control in ControlHelper::MyWndProc");
01171 
01172     if (pEntry != NULL)
01173     {
01174         switch (Msg)
01175         {
01176             case WM_MOUSEMOVE:
01177             {
01178         
01179                 // start control drag
01180                 if(KeyPress::IsAlternativePressed())
01181                 {
01182                     return TRUE;
01183                 }
01184                 // Update the status bar on MouseMove messages
01185                 String_256 Desc;
01186                 if (pEntry->ControlStatusLineText(&Desc))
01187                 {
01188                     StatusLine* pStatusLine=GetApplication()->GetpStatusLine();
01189                     if (pStatusLine->AllowControlHelp ())
01190                     {
01191                         if (pStatusLine)
01192                             pStatusLine->UpdateText(&Desc,STATUSLINE_SELDESC_BUTTONS);
01193                     }
01194                 }
01195             }
01196             break;
01197   
01198             case WM_SETFOCUS:
01199                 // This control has focus - disable the bubble help.
01200                 //ControlHasFocus = TRUE;
01201                 if (pEntry->AddCommitHandling)
01202                 {
01203                     SendMessage(GetParent(GetParent(Window)),WM_CTL_SETFOCUS,TRUE,(LPARAM)Window);  
01204                     SendMessage(GetParent(Window),WM_CTL_SETFOCUS,TRUE,(LPARAM)Window);
01205                     
01206                     // Send the control an EM_SETSEL message so the text within it is highlighted.
01207                     ::SendMessage(Window, EM_SETSEL, 0, (LPARAM) (INT32) -1);
01208                 }
01209 
01210                 // Fall through...
01211             
01212             case WM_LBUTTONDOWN:
01213                 if(KeyPress::IsAlternativePressed())
01214                 {
01215                     String_256 ClassNameStr;  // The control type
01216                     GetClassName(GetParent(Window), (TCHAR*)ClassNameStr, 255);
01217                     if (ClassNameStr == String_8(TEXT("ComboBox")))
01218                     {
01219                         SendMessage(GetParent(GetParent(Window)),WM_START_CTLDRAG,
01220                                         KeyPress::IsConstrainPressed(),(LPARAM)GetParent(Window));  
01221                     }
01222                     else
01223                     {
01224                         SendMessage(GetParent(Window),WM_START_CTLDRAG,
01225                                         KeyPress::IsConstrainPressed(),(LPARAM)Window);
01226                     }
01227                     return TRUE;
01228                 }
01229             case WM_MBUTTONDOWN:
01230             case WM_RBUTTONDOWN:
01231             case WM_LBUTTONUP:
01232             case WM_MBUTTONUP:
01233             case WM_RBUTTONUP:
01234             case WM_LBUTTONDBLCLK:
01235             case WM_MBUTTONDBLCLK:
01236             case WM_RBUTTONDBLCLK:
01237         
01238                 // start control drag
01239             
01240                 // Mouse activity - disable bubble help.
01241                 BubbleHelpDisable();
01242                 break;
01243 
01244             case WM_KILLFOCUS:
01245                 // This control has lost focus - allow the bubble help to start working again.
01246                 ControlHasFocus = FALSE;
01247                 if (pEntry->AddCommitHandling)
01248                 {
01249                     SendMessage(GetParent(GetParent(Window)),WM_CTL_SETFOCUS,FALSE,(LPARAM)Window); 
01250                     SendMessage(GetParent(Window),WM_CTL_SETFOCUS,FALSE,(LPARAM)Window);    
01251                 }
01252                 break;
01253             default:
01254 
01255                 if (pEntry->AddCommitHandling)
01256                 {
01257                     // Is commit processing required, this should be the case if the control 
01258                     // is an edit or ComboBox 
01259 
01260                     // Determine the control type
01261                     switch (Msg)
01262                     {
01263                         case WM_CHAR:
01264                             // Process this message to avoid message beeps.
01265                             if ((wParam == CAMKEY(RETURN)) || (wParam == CAMKEY(TAB)))
01266                             {
01267                                 ControlHasFocus = FALSE;
01268                                 return FALSE;
01269                             }
01270                             break;
01271 
01272                         case WM_GETDLGCODE:
01273                             return DLGC_WANTALLKEYS;
01274 
01275                         case WM_KEYDOWN:
01276                             // escape should return the focus back to the main frame
01277                             if (wParam == CAMKEY(ESCAPE))
01278                             {
01279                                 ControlHasFocus = FALSE;
01280                                 DialogManager::DefaultKeyboardFocus();
01281                                 return FALSE;
01282                             }
01283                             else if (wParam == CAMKEY(RETURN) || wParam == CAMKEY(TAB))
01284                             {
01285                                 wxWindow* BarWnd;
01286                                 wxWindow* CtlWnd; 
01287                     
01288                                 String_256 ClassNameStr;  // The control type
01289 
01290                                 BOOL camelotCustomComboBox = FALSE;
01291 
01293                                 
01294                                 // need to do things slightly differently if the control is a 
01295                                 // cc_1dBitmapComboBoxEdit or cc_2dBitmapComboBoxEdit
01296 
01297                                 GetClassName (Window, (TCHAR*)ClassNameStr, 255);
01298 
01299                                 if ((ClassNameStr == String_64(TEXT("cc_1dBitmapComboBoxEdit"))) ||
01300                                     (ClassNameStr == String_64(TEXT("cc_2dBitmapComboBoxEdit"))))
01301                                 {
01302                                     camelotCustomComboBox = TRUE;
01303                                 }
01304 
01306 
01307                                 if (camelotCustomComboBox == FALSE)
01308                                 {
01309                                     GetClassName(GetParent(Window), (TCHAR*)ClassNameStr, 255);
01310 
01311                                     if (ClassNameStr == String_8(TEXT("ComboBox")) || ClassNameStr == String_16(TEXT("cc_CustomEdit")))
01312                                     {
01313                                         // edit controls of these classes are children of the controls.
01314                                         BarWnd = GetParent(GetParent(Window));  // Because combo edit control
01315                                                                             // is a child of the combo 
01316                                                                             // control itself
01317                                         CtlWnd = GetParent(Window);
01318                                     }
01319                                     else
01320                                     {
01321                                         // It's an Edit control
01322                                         BarWnd = GetParent(Window);  // Bar is the edit's parent
01323                                         CtlWnd = Window;
01324                                     }
01325                                 }
01326                                 else
01327                                 {
01328                                     BarWnd = GetParent (Window);
01329                                     CtlWnd = Window;
01330 
01331                                     // the following sendmessage is actually so that when CAMKEY(RETURN) is hit,
01332                                     // (which actually goes through the WM_KEYDOWN in ctrlhelp.cpp); that we can actually
01333                                     // set the selected item BEFORE dialog manager continues !
01334 
01335                                     if (wParam == CAMKEY(RETURN))
01336                                     {
01337                                         SendMessage (CtlWnd, CB_SETTOPINDEX, 1, 0);
01338                                     }
01339                                     else
01340                                     {
01341                                         SendMessage (CtlWnd, CB_SETTOPINDEX, 0, 0);
01342                                     }
01343                                 }
01344 
01345                                 if (wParam == CAMKEY(RETURN) )
01346                                 {
01347                                     ControlHasFocus = FALSE;
01348                                     SendMessage(BarWnd, WM_CTL_SETFOCUS, COMMIT, (LPARAM) Window);  
01349                                 }                                               
01350 
01351                                 // Send commit message to parent, passing the key code (TAB or ENTER) which
01352                                 // it might be useful to know.
01353                                 SendMessage(BarWnd, WM_CTL_COMMIT, GetWindowID(CtlWnd), (LPARAM) wParam);
01354                                 
01355                                 // To fix various bugs this code is a little bodged.  The problems
01356                                 // that this code tries to address are:
01357                                 //  1)  Clicking in one of the size edit controls in the selector info bar,
01358                                 //      entering some text, pressing enter and then clicking in the same edit
01359                                 //      control a second time (this was refusing to take the focus a second
01360                                 //      time)
01361                                 //  2)  Typing a font name in the font drop down box box and then pressing
01362                                 //      enter (this was returning to the font the user had selected before
01363                                 //      they started typeing)
01364                                 if (::GetDlgCtrlID(Window) != _R(IDC_FONT_COMBO))
01365                                 {
01366                                     if (wParam == CAMKEY(RETURN))
01367                                     {
01368                                         GetMainFrame()->SetFocus();
01369                                     }
01370                                     else
01371                                     {
01372                                         // Move focus to the next (previous) control, just like Windows.
01373                                         wxWindow* wxWindow*NewFocus = ::GetNextDlgTabItem(BarWnd, Window, ::GetKeyState(CAMKEY(SHIFT)) < 0);
01374                                         if (wxWindow*NewFocus != NULL) ::SetFocus(wxWindow*NewFocus);
01375                                     }
01376                                     return FALSE;
01377                                 }
01378                             }
01379                             // added by Karim 30/9/99, to allow a more intelligent font-combo
01380                             else if (wParam >= 'A' && wParam <= 'Z' && ::GetDlgCtrlID(Window) == _R(IDC_FONT_COMBO))
01381                             {
01382                                 // pass the message on to the combo-box's parent, to be
01383                                 // processed in DialogManager::SendDialogMessage.
01384                                 ::SendMessage(GetParent(Window), WM_KEYDOWN, wParam, lParam);
01385                             }
01386                             break;
01387                     }
01388 
01389                 }
01390         }
01391     }
01392 
01393     // For any other messages, kill bubble help before passing them on.
01394 //  if (BubbleHelpActive)
01395 //      BubbleHelpKill();
01396 
01397     // Default to chaining to the normal WndProc
01398     return ::CallWindowProc(pEntry->WndProc, Window, Msg, wParam, lParam);
01399 }
01400 
01401 
01402 /********************************************************************************************
01403 
01404 >   void ControlHelper::ServiceBubbleHelp()
01405 
01406     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
01407     Created:    26/04/94
01408     Purpose:    Updates the bubble help for our controls.  Should be called on idle or
01409                 timer events.
01410 
01411 ********************************************************************************************/
01412 
01413 void ControlHelper::ServiceBubbleHelp()
01414 {
01415     // Find out which window the cursor is over.
01416     POINT Pos;
01417     ::GetCursorPos(&Pos);
01418     wxWindow* ThisControl = ::WindowFromPoint(Pos);
01419 
01420     if ((ThisControl != NULL) /*&& (IsWindowVisible (ThisControl))*/)
01421     {
01422         // This code copes with disabled controls...
01423 
01424         // To look at the child windows of wxWindow*, screen coordinates
01425         // need to be converted to client coordinates.
01426         POINT ClientPos = Pos;
01427         ScreenToClient(ThisControl, &ClientPos);
01428 
01429         // Search through all child windows at this point. This
01430         // will continue until no child windows remain.
01431         while (TRUE)
01432         {
01433             wxWindow* Child = ChildWindowFromPoint(ThisControl, ClientPos);
01434 
01435             if ((Child != NULL) && (Child != ThisControl))
01436                 ThisControl = Child;
01437             else
01438                 break;
01439         }
01440     }
01441 
01442 
01443     WNDPROC WndProc;
01444 
01445     if (ThisControl == AdHocWindow)
01446         // Special ad-hoc bubble help - bodge the WndProc to fool the state machine.
01447         WndProc = ControlHelper::MyWndProc;
01448     else
01449     {
01450         // *** BODGE FIX by JustinF.
01451         // For some reason GetWindowLong GP-faults , so check the params to the call below.
01452         // IsWindow clears the error state so get it now just in case...
01453         DWORD LastError = ::GetLastError();
01454         if (!::IsWindow(ThisControl))
01455         {
01456             TRACE( _T("BubbleHelp wxWindow* bug - ThisControl is invalid (0x%p), GetLastError is 0x%lX\n"),
01457                     (LPVOID) ThisControl, LastError);
01458             return;
01459         }
01460         
01461         // Find out the WndProc for this window.
01462         WndProc = (WNDPROC) ::GetWindowLong(ThisControl, GWL_WNDPROC);
01463 
01464         // If it's one of ours, find out if this is a child window of a control.
01465         // Only bother if this is different to the last control we looked at (because
01466         // we will have done this same check for the last control, so it must be a proper 
01467         // parent control if it's the same as the last control).
01468         if ((ThisControl != LastControl) && (WndProc == MyWndProc))
01469         {
01470             ControlEntry *pEntry = Controls->FindControl(ThisControl);
01471             ENSURE(pEntry != NULL, "Catastrophic failure in bubble help");
01472 
01473             // Just use the main parent handle of this control (may actually be the
01474             // same as the value ThisControl currently holds anyway).
01475             ThisControl = pEntry->Parent;
01476         }
01477     }
01478 
01479     //
01480     // Enter state machine event processor...
01481     //
01482     BubbleHelpStateMachine(Pos, ThisControl, WndProc);
01483 }
01484 
01485 /********************************************************************************************
01486 
01487 >   void ControlHelper::BubbleHelpStateMachine(POINT Pos, wxWindow* ThisControl, WNDPROC WndProc)
01488 
01489     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
01490     Created:    30/06/94
01491     Inputs:     Pos - the current cursor position; used to detect how long the mouse has
01492                       been stationary for. Currently this is not used as Charles doesn't
01493                       like it if bubble doesn't come up if you continuously jaggle the
01494                       pointer about.
01495                 ThisControl - window handle of the control that the pointer is over.
01496                 WndProc - WindowProc of the control specified by ThisControl (this enables
01497                           this routine to work out if this control has been subclassed by
01498                           the ControlHelper system).
01499     Purpose:    Service the bubble help state machine.  Given the input parameters it
01500                 works out whether it needs to change state, and which state to change to.
01501                 State changing also depends on timers elapsing and so on.
01502     SeeAlso:    -
01503 
01504 ********************************************************************************************/
01505 
01506 void ControlHelper::BubbleHelpStateMachine(POINT Pos, wxWindow* ThisControl, WNDPROC WndProc)
01507 {
01508     // can sometimes be called with a NULL handle if pointer has found its way offscreen
01509     if(ThisControl== NULL)
01510         return;
01511 
01512     switch (BubbleHelpState)
01513     {
01514         case STATE_DEAD:
01515             if (WndProc == ControlHelper::MyWndProc)
01516             {
01517                 // It's one of our controls - set update pending
01518                 SetState(STATE_INITIAL_PENDING);
01519             }
01520             else if (Bars->IsABar(ThisControl))
01521             {
01522                 // It's a Camelot bar - set to initial disabled.
01523                 SetState(STATE_INITIAL_DISABLED);
01524             }
01525             else
01526             {
01527                 // this means that the cursor has left all the bars alone
01528                 // so now is a good time to remove that button plinth if we were
01529                 // using the flat style buttons with active plinth/bevelling (sjk 24/2/00)
01530                 if (LastPointerInButton)
01531                 {
01532                     INT32 value = GetWindowLong(LastPointerInButton, GWL_STYLE);
01533                     if (value & BS_POINTEROVER)
01534                     {
01535                         // extra tests on position since the export dlg appears to use the
01536                         // help system slightly differently!!!
01537                         RECT r;
01538                         GetWindowRect(LastPointerInButton, &r);
01539                         POINT p;
01540                         GetCursorPos(&p);
01541 
01542                         if (p.x < r.left || p.x > r.right || p.y < r.top || p.y > r.bottom)
01543                         {
01544                             SetWindowLong(LastPointerInButton, GWL_STYLE, value & ~BS_POINTEROVER);
01545                             ::InvalidateRect(LastPointerInButton, NULL, FALSE);
01546                             ::UpdateWindow(LastPointerInButton);
01547                             LastPointerInButton = NULL;
01548                         }
01549                     }
01550                 }
01551             }
01552             break;
01553 
01554         case STATE_INITIAL_PENDING:
01555             if (WndProc == ControlHelper::MyWndProc)
01556             {
01557                 // User is over our control - if delay has passed and none of our controls
01558                 // have focus, set to active
01559                 if (PendingTimer.Elapsed(800) && !ControlHasFocus)
01560                 {
01561                     SetState(STATE_ACTIVE, ThisControl);
01562                 }
01563             }
01564             else if (Bars->IsABar(ThisControl))
01565             {
01566                 // User is on a control bar - disable help.
01567                 SetState(STATE_INITIAL_DISABLED);
01568             }
01569             else
01570             {
01571                 // User is not on a control or a bar - kill bubble help.
01572                 SetState(STATE_DEAD);
01573             }
01574             break;
01575 
01576         case STATE_INITIAL_DISABLED:
01577             if (WndProc == ControlHelper::MyWndProc)
01578             {
01579                 // User is over our control - is it different to the last control
01580                 // we looked at?
01581                 if (ThisControl != LastControl)
01582                 {
01583                     // Yes - start the delay timer for displaying bubble help
01584                     SetState(STATE_INITIAL_PENDING);
01585                 }
01586                 else
01587                 {
01588                     // No - bubble help must be disabled for this control so do nothing.
01589                 }
01590             }
01591             else if (!Bars->IsABar(ThisControl))
01592             {
01593                 // User is not on a control or a bar - kill bubble help.
01594                 SetState(STATE_DEAD);
01595             }
01596             break;
01597 
01598         case STATE_UPDATE_PENDING:
01599             if (WndProc == ControlHelper::MyWndProc)
01600             {
01601                 // User is over our control - if delay has passed, set to active (but only 
01602                 // if none of our controls have focus).
01603                 if (PendingTimer.Elapsed(100) && !ControlHasFocus)
01604                 {
01605                     SetState(STATE_ACTIVE, ThisControl);
01606                 }
01607             }
01608             else if (Bars->IsABar(ThisControl))
01609             {
01610                 // User is on a control bar - disable help.
01611                 SetState(STATE_DISABLED);
01612             }
01613             else
01614             {
01615                 // User is not on a control or a bar - kill bubble help.
01616                 SetState(STATE_DEAD);
01617             }
01618             break;
01619 
01620         case STATE_ACTIVE:
01621             if (BubbleWnd->GetSafewxWindow*() == ThisControl)
01622             {
01623                 // We're over our own bubble window - disable it for a bit.
01624                 SetState(STATE_UPDATE_PENDING);
01625 
01626                 // We don't want to update LastControl or LastPos.
01627                 return;
01628             }
01629             else if (WndProc == ControlHelper::MyWndProc)
01630             {
01631                 // User is over our control - if a different one, set delayed update,
01632                 // otherwise no change.
01633                 // NB. if ad-hoc is active, we check different variables
01634                 if (ThisControl == AdHocWindow)
01635                 {
01636                     // Ad-hoc window - check the flag set by DoBubbleHelpOn().
01637                     if (AdHocControlIsDifferent)
01638                         SetState(STATE_UPDATE_PENDING);
01639                 }
01640                 else if (LastControl != ThisControl)
01641                 {
01642                     SetState(STATE_UPDATE_PENDING);
01643                 }
01644             }
01645             else if (Bars->IsABar(ThisControl))
01646             {
01647                 // User is on a control bar - disable help.
01648                 SetState(STATE_DISABLED);
01649             }
01650             else
01651             {
01652                 // User is not on a control or a bar - kill bubble help.
01653                 SetState(STATE_DEAD);
01654             }
01655             break;
01656 
01657         case STATE_DISABLED:
01658             if (WndProc == ControlHelper::MyWndProc)
01659             {
01660                 // User is over our control - is it different to the last control
01661                 // we looked at?
01662                 // NB. if ad-hoc is active, we check different variables
01663                 if (ThisControl == AdHocWindow)
01664                 {
01665                     // Ad-hoc window - check the flag set by DoBubbleHelpOn().
01666                     if (AdHocControlIsDifferent)
01667                         SetState(STATE_UPDATE_PENDING);
01668                 }
01669                 else if (LastControl != ThisControl)
01670                 {
01671                     // Yes - start the delay timer for displaying bubble help
01672                     SetState(STATE_UPDATE_PENDING);
01673                 }
01674                 else
01675                 {
01676                     // No - bubble help must be disabled for this control so do nothing.
01677                 }
01678             }
01679             else if (!Bars->IsABar(ThisControl))
01680             {
01681                 // User is not on a control or a bar - kill bubble help.
01682                 SetState(STATE_DEAD);
01683             }
01684             break;
01685 
01686         default:
01687             ENSURE(FALSE, "Bad current state in bubble help");
01688             break;
01689     }
01690 
01691     // Update the cursor position for pending updates, and the last control position
01692     // for display updates.
01693     LastPos = Pos;
01694     LastControl = ThisControl;
01695 }
01696 
01697 
01698 
01699 /********************************************************************************************
01700 
01701 >   void ControlHelper::SetState(BubbleState NewState, wxWindow* Window)
01702 
01703     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
01704     Created:    30/06/94
01705     Inputs:     NewState - the state to change the state machine to.
01706                 Window - window handle of the control the pointer is currently over (only
01707                          used when setting the state to STATE_ACTIVE, when it is used to
01708                          get the bubble help text).
01709     Purpose:    Change the state of the bubble help state machine to the specified state.
01710                 Calls the appropriate state change handler to process the event.
01711     SeeAlso:    ControlHelper::DeadHandler; ControlHelper::ActiveHandler; 
01712                 ControlHelper::InitialPendingHandler; ControlHelper::InitialDisabledHandler;
01713                 ControlHelper::DisabledHandler; ControlHelper::UpdatePendingHandler
01714 
01715 ********************************************************************************************/
01716 
01717 void ControlHelper::SetState(BubbleState NewState, wxWindow* Window)
01718 {
01719     // Find out which state to change to.
01720     switch (NewState)
01721     {
01722         case STATE_DEAD:                DeadHandler();              break;
01723         case STATE_ACTIVE:              ActiveHandler(Window);      break;
01724         case STATE_INITIAL_PENDING:     InitialPendingHandler();    break;
01725         case STATE_INITIAL_DISABLED:    InitialDisabledHandler();   break;
01726         case STATE_UPDATE_PENDING:      UpdatePendingHandler();     break;
01727         case STATE_DISABLED:            DisabledHandler();          break;
01728     }
01729 }
01730 
01731 
01732 /********************************************************************************************
01733 
01734 >   void ControlHelper::DeadHandler()
01735 
01736     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
01737     Created:    30/06/94
01738     Purpose:    Kill off the bubble help by setting the bubble help state machine to the
01739                 STATE_DEAD state.
01740     SeeAlso:    ControlHelper::ActiveHandler; ControlHelper::InitialPendingHandler; 
01741                 ControlHelper::InitialDisabledHandler; ControlHelper::DisabledHandler;
01742                 ControlHelper::UpdatePendingHandler
01743 
01744 ********************************************************************************************/
01745 
01746 void ControlHelper::DeadHandler()
01747 {
01748 #if STATE_TRACE
01749     if (IsUserName("Tim"))
01750         TRACE( _T("STATE_DEAD\n"));
01751 #endif
01752 
01753     switch (BubbleHelpState)
01754     {
01755         case STATE_ACTIVE:
01756             // Kill the bubble help window and fall through.
01757             ENSURE(BubbleWnd != NULL, "No bubble help window!");
01758             BubbleHelpKill();
01759 
01760         case STATE_INITIAL_PENDING:
01761         case STATE_INITIAL_DISABLED:
01762         case STATE_DISABLED:
01763         case STATE_UPDATE_PENDING:
01764             BubbleHelpState = STATE_DEAD;
01765             break;
01766 
01767         default:
01768             ENSURE(FALSE, "Wrong state in bubble help");
01769             break;
01770     }
01771 }
01772 
01773 /********************************************************************************************
01774 
01775 >   void ControlHelper::ActiveHandler(wxWindow* Window)
01776 
01777     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
01778     Created:    30/06/94
01779     Inputs:     Window - handle of the control the cursor is currently over.
01780     Purpose:    Activate the bubble help system by moving to the STATE_ACTIVE state.
01781                 This causes a bubble help window to appear under the cursor, but not is
01782                 there are any keys down, or if there is a drag going on, or if our app
01783                 is not active.  At some time in the future we should also refuse to put
01784                 up bubble help if the user has the menu up.
01785     SeeAlso:    ControlHelper::DeadHandler; ControlHelper::InitialPendingHandler; 
01786                 ControlHelper::InitialDisabledHandler; ControlHelper::DisabledHandler;
01787                 ControlHelper::UpdatePendingHandler
01788 
01789 ********************************************************************************************/
01790 
01791 void ControlHelper::ActiveHandler(wxWindow* Window)
01792 {
01793     // Sanity checks
01794     ENSURE(Window != NULL, "Bad handle in control helper ActiveHandler()");
01795     ENSURE(BubbleWnd == NULL, "Bubble window already exists in BubbleHelpStart()");
01796 
01797     // No bubble help during drags, or if any mouse buttons are down.
01798 
01799     // Get mouse keys - top bit is set if the button is down.
01800     INT8 MouseStatus = (::GetKeyState(CAMKEY(LBUTTON)) | 
01801                          ::GetKeyState(CAMKEY(MBUTTON)) | 
01802                          ::GetKeyState(CAMKEY(RBUTTON)));
01803 
01804     if ((MouseStatus & 0x8000) ||
01805         (Operation::GetCurrentDragOp() != NULL))
01806     {
01807         // Revert to pending
01808         BubbleHelpState = STATE_ACTIVE; // Very important else state machine gets upset!
01809         SetState(STATE_INITIAL_PENDING);
01810         return;
01811     }
01812 
01813     // Don't do bubble help if we're iconised.
01814     CWnd *pMainWnd = AfxGetApp()->m_pMainWnd;
01815     if (pMainWnd->IsIconic())
01816     {
01817         // Revert to pending
01818         BubbleHelpState = STATE_ACTIVE; // Very important else state machine gets upset!
01819         SetState(STATE_INITIAL_PENDING);
01820         return;
01821     }
01822 
01823     // Only do bubble help if our application is active.
01824     wxWindow* Active  = ::GetActiveWindow();
01825     wxWindow* MainWnd = pMainWnd->m_wxWindow*;
01826 
01827     if ((Active != MainWnd) && (::GetParent(Active) != MainWnd))
01828     {
01829         // Revert to pending
01830         BubbleHelpState = STATE_ACTIVE; // Very important else state machine gets upset!
01831         SetState(STATE_INITIAL_PENDING);
01832         return;
01833     }
01834 
01835     // Don't do bubble help if modal dialog is open.
01836     if (DialogManager::ModalDialogOpen() || (ModalDialogs > 0))
01837     {
01838         // Revert to pending
01839         BubbleHelpState = STATE_ACTIVE; // Very important else state machine gets upset!
01840         SetState(STATE_INITIAL_PENDING);
01841         return;
01842     }
01843 
01844     // Don't do bubble help if a menu is up - Windows captures the mouse during a menu
01845     // processing loop.
01846     if (::GetCapture() != NULL)
01847     {
01848         // Revert to pending
01849         BubbleHelpState = STATE_ACTIVE; // Very important else state machine gets upset!
01850         SetState(STATE_INITIAL_PENDING);
01851         return;
01852     }
01853 
01854     // also only do bubble help if the control were over is actually visible
01855     if (::IsWindowVisible (Window) == FALSE)
01856     {
01857         // Revert to pending
01858         BubbleHelpState = STATE_ACTIVE; // Very important else state machine gets upset!
01859         SetState(STATE_INITIAL_PENDING);
01860         return;
01861     }
01862 
01863     // Ok - we're allowed to display bubble help - work out what to do:
01864 
01865 #if STATE_TRACE
01866     if (IsUserName("Tim"))
01867         TRACE( _T("STATE_ACTIVE\n"));
01868 #endif
01869 
01870     switch (BubbleHelpState)
01871     {
01872         case STATE_INITIAL_PENDING:
01873         case STATE_UPDATE_PENDING:
01874             BubbleHelpState = STATE_ACTIVE;
01875 
01876             // Create and show a bubble help window
01877             ENSURE(BubbleWnd == NULL, "Bubble window already exists!");
01878     
01879             // Make a new bubble help window
01880             TRY
01881             {
01882                 BubbleWnd = new BubbleHelpWnd;
01883             }
01884             CATCH(CMemoryException, e)
01885             {
01886                 TRACEALL( _T("Unable to create bubble help window!\n"));
01887                 return;
01888             }
01889             END_CATCH
01890 
01891             // Create the actual window
01892             if (!BubbleWnd->Create())
01893             {
01894                 TRACEALL( _T("Could not Init bubble help window\n"));
01895                 return;
01896             }
01897 
01898             // If ad-hoc bubble help, call provider to get required text, otherwise
01899             // get resource IDs out of the OpDescriptor in our table of controls.
01900             if ((AdHocCallback != NULL) && (Window == AdHocWindow))
01901             {
01902                 char *BubbleText = AdHocCallback(AdHocWindow, AdHocControl, AdHocReference);
01903                 if (BubbleWnd->SetText(BubbleText))
01904                     BubbleWnd->Show();
01905             }
01906             else
01907             {
01908                 // Find the record for this control.
01909                 ControlEntry *pEntry;
01910                 pEntry = Controls->FindControl(Window);
01911 
01912                 ENSURE(pEntry != NULL, "Bad Entry in ActiveHandler");
01913 
01914                 // If we have an OpDescriptor, then get the bubble help/module IDs from that,
01915                 // otherwise get them directly out of the ControlEntry.
01916                 UINT32 BubbleID;
01917                 UINT32 ModuleID;
01918 
01919                 if (pEntry->pOpDesc != NULL)
01920                 {
01921                     BubbleID = pEntry->pOpDesc->GetBubbleId();
01922                     ModuleID = Tool::GetModuleID(pEntry->pOpDesc->GetToolID());
01923                 }
01924                 else
01925                 {
01926                     BubbleID = pEntry->BubbleID;
01927                     ModuleID = pEntry->ModuleID;
01928                 }
01929 
01930                 // Load the bubble help text.
01931                 // Ugly hack, but who cares?
01932                 String_256 strCaption;
01933                 GetWindowText(Window, strCaption, 255);
01934                 if (strCaption == String_256("Download"))
01935                 {
01936                     if (BubbleWnd->SetText("Download this resource"))
01937                     // If the string loaded ok, put the window in the correct place and show it.
01938                     BubbleWnd->Show();
01939                 }
01940                 else
01941                 {
01942                     if (BubbleWnd->SetText(BubbleID, ModuleID))
01943                     // If the string loaded ok, put the window in the correct place and show it.
01944                     BubbleWnd->Show();
01945                 }
01946             }
01947 
01948             break;
01949 
01950         default:
01951             ENSURE(FALSE, "Wrong state in bubble help");
01952             break;
01953     }
01954 }
01955 
01956 /********************************************************************************************
01957 
01958 >   void ControlHelper::InitialPendingHandler()
01959 
01960     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
01961     Created:    30/06/94
01962     Purpose:    Sets the state to STATE_INITIAL_PENDING, which means that the bubble help
01963                 window is about to come up, when a timer has elapsed, providing the user
01964                 doesn't move the cursor away or press a key etc.
01965     SeeAlso:    ControlHelper::DeadHandler; ControlHelper::ActiveHandler; 
01966                 ControlHelper::InitialDisabledHandler; ControlHelper::DisabledHandler;
01967                 ControlHelper::UpdatePendingHandler
01968 
01969 ********************************************************************************************/
01970 
01971 void ControlHelper::InitialPendingHandler()
01972 {
01973 #if STATE_TRACE
01974     if (IsUserName("Tim"))
01975         TRACE( _T("STATE_INITIAL_PENDING\n"));
01976 #endif
01977 
01978     switch (BubbleHelpState)
01979     {
01980         case STATE_ACTIVE:
01981             // Kill off bubble help (if it exists) and fall through
01982             BubbleHelpKill();
01983 
01984         case STATE_DEAD:
01985         case STATE_INITIAL_DISABLED:
01986             // Sample timer (i.e. reset delay)
01987             PendingTimer.Sample();
01988             BubbleHelpState = STATE_INITIAL_PENDING;
01989             break;
01990 
01991         default:
01992             ENSURE(FALSE, "Wrong state in bubble help");
01993             break;
01994     }
01995 }
01996 
01997 /********************************************************************************************
01998 
01999 >   void ControlHelper::InitialDisabledHandler()
02000 
02001     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
02002     Created:    30/06/94
02003     Purpose:    Sets the bubble help state to STATE_INITIAL_DISABLED, which means that the
02004                 cursor is over a Camelot bar but not over a control, or that for some
02005                 reason (key press, focus change etc) bubble help should not be displayed,
02006                 even though the cursor is over a control that supports bubble help.
02007     SeeAlso:    ControlHelper::DeadHandler; ControlHelper::ActiveHandler; 
02008                 ControlHelper::InitialPendingHandler; ControlHelper::DisabledHandler;
02009                 ControlHelper::UpdatePendingHandler
02010 
02011 ********************************************************************************************/
02012 
02013 void ControlHelper::InitialDisabledHandler()
02014 {
02015 #if STATE_TRACE
02016     if (IsUserName("Tim"))
02017         TRACE( _T("STATE_INITIAL_DISABLED\n"));
02018 #endif
02019 
02020     // If a control has focus, then don't change state unless it's in order to die.
02021     if (ControlHasFocus)
02022     {
02023         if (BubbleHelpState != STATE_DEAD)
02024             return;
02025     }
02026 
02027     switch (BubbleHelpState)
02028     {
02029         case STATE_DEAD:
02030         case STATE_INITIAL_PENDING:
02031             // Don't do anything.
02032             BubbleHelpState = STATE_INITIAL_DISABLED;
02033             break;
02034 
02035         default:
02036             ENSURE(FALSE, "Wrong state in bubble help");
02037             break;
02038     }
02039 }
02040 
02041 /********************************************************************************************
02042 
02043 >   void ControlHelper::UpdatePendingHandler()
02044 
02045     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
02046     Created:    30/06/94
02047     Purpose:    Set the bubble help state to STATE_UPDATE_PENDING, which means that the
02048                 bubble help will appear very quickly if moved over a control.  This is used
02049                 when the user moves from a control to another control, so it enables the 
02050                 user to move along the controls and browse the bubble text for them quite 
02051                 quickly.
02052     SeeAlso:    ControlHelper::DeadHandler; ControlHelper::ActiveHandler; 
02053                 ControlHelper::InitialPendingHandler; ControlHelper::InitialDisabledHandler;
02054                 ControlHelper::DisabledHandler
02055 
02056 ********************************************************************************************/
02057 
02058 void ControlHelper::UpdatePendingHandler()
02059 {
02060 #if STATE_TRACE
02061     if (IsUserName("Tim"))
02062         TRACE( _T("STATE_UPDATE_PENDING\n"));
02063 #endif
02064 
02065     switch (BubbleHelpState)
02066     {
02067         case STATE_ACTIVE:
02068             // Kill the bubble help window and fall through.
02069             // NB. There may not really be a bubble help window, as we may have come via a
02070             // brief 'dummy' ACTIVE state (i.e. ACTIVE refused to be the current state
02071             // because a mouse button was down or we don't have focus etc).
02072             BubbleHelpKill();
02073 
02074         case STATE_DISABLED:
02075             // Sample the pending timer
02076             PendingTimer.Sample();
02077             BubbleHelpState = STATE_UPDATE_PENDING;
02078             break;
02079 
02080         default:
02081             ENSURE(FALSE, "Wrong state in bubble help");
02082             break;
02083     }
02084 }
02085 
02086 /********************************************************************************************
02087 
02088 >   void ControlHelper::DisabledHandler()
02089 
02090     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
02091     Created:    30/06/94
02092     Purpose:    Set the bubble help state to STATE_DISABLED, which means that the
02093                 bubble help will appear very quickly if moved over a control.  This is used
02094                 when the user moves from a control to a Camelot bar, so it enables the 
02095                 user to move between bars (or even controls which are separated on a bar)
02096                 and browse the bubble text for them quite quickly.
02097     SeeAlso:    ControlHelper::DeadHandler; ControlHelper::ActiveHandler; 
02098                 ControlHelper::InitialPendingHandler; ControlHelper::InitialDisabledHandler;
02099                 ControlHelper::UpdatePendingHandler
02100 
02101 ********************************************************************************************/
02102 
02103 void ControlHelper::DisabledHandler()
02104 {
02105 #if STATE_TRACE
02106     if (IsUserName("Tim"))
02107         TRACE( _T("STATE_DISABLED\n"));
02108 #endif
02109 
02110     // If a control has focus, then don't change state unless it's in order to die.
02111     if (ControlHasFocus)
02112     {
02113         if (BubbleHelpState != STATE_DEAD)
02114             return;
02115     }
02116 
02117     switch (BubbleHelpState)
02118     {
02119         case STATE_ACTIVE:
02120             // Kill the bubble help window and fall through.
02121             ENSURE(BubbleWnd != NULL, "No bubble help window!");
02122             BubbleHelpKill();
02123 
02124         case STATE_UPDATE_PENDING:
02125             BubbleHelpState = STATE_DISABLED;
02126             break;
02127 
02128         default:
02129             ENSURE(FALSE, "Wrong state in bubble help");
02130             break;
02131     }
02132 }
02133 
02134 /********************************************************************************************
02135 
02136 >   void ControlHelper::BubbleHelpDisable()
02137 
02138     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
02139     Created:    30/06/94
02140     Purpose:    Cause the bubble help to go into its 'disabled' state, so that even if it
02141                 is over a control that supports bubble help, bubble help will not be 
02142                 displayed.  If bubble help is currently displayed, it is removed.
02143                 This function should be called on events that ought to kill off bubble
02144                 help, e.g. key presses, focus switches etc.
02145     SeeAlso:    ControlHelper::BubbleHelpKill
02146 
02147 ********************************************************************************************/
02148 
02149 void ControlHelper::BubbleHelpDisable()
02150 {
02151     // If bubble help is around, disable it.
02152     switch (BubbleHelpState)
02153     {
02154         case STATE_ACTIVE:
02155         case STATE_UPDATE_PENDING:
02156             // Bubble help is on screen or will be RSN so slide into the disabled
02157             // state in order to get rid of/avoid it.
02158             SetState(STATE_DISABLED);
02159             break;
02160 
02161         case STATE_INITIAL_PENDING:
02162             // Bubble help will be on screen RSN so slide into the initial disabled
02163             // state in order to get rid of/avoid it.
02164             SetState(STATE_INITIAL_DISABLED);
02165             break;
02166     }
02167 }
02168 
02169 /********************************************************************************************
02170 
02171 >   void ControlHelper::BubbleHelpKill()
02172 
02173     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
02174     Created:    30/06/94
02175     Purpose:    Low level function to kill off the bubble help window and delete it, and
02176                 set various variables for ad-hoc bubble help to sensible 'null' values.
02177     SeeAlso:    ControlHelper::BubbleHelpDisable
02178     Scope:      Private
02179 
02180 ********************************************************************************************/
02181 
02182 void ControlHelper::BubbleHelpKill()
02183 {
02184     // Destroy the bubble help window.
02185     if (BubbleWnd != NULL)
02186     {
02187         BubbleWnd->Hide();
02188         delete BubbleWnd;
02189         BubbleWnd = NULL;
02190     }
02191 
02192     // Disable ad-hoc bubble help.
02193     // NB we don't change the AdHocControl, because then we know that we're over the same
02194     // control and should remain disabled (e.g. if user clicks on button and bubble help
02195     // goes away, it shouldn't come back until they move to another control).
02196     AdHocWindow = NULL;
02197     AdHocCallback = NULL;
02198     AdHocReference = NULL;
02199 }
02200 
02201 /********************************************************************************************
02202 
02203 >   void ControlHelper::DoBubbleHelpOn(wxWindow* Window, UINT32 NewControl, 
02204                                        BubbleHelpCallback Callback, void *Reference)
02205 
02206     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
02207     Created:    30/06/94
02208     Inputs:     Window - the window to provide bubble help on.
02209                 NewControl - pseudo-ID of the imaginary control - this is so the bubble
02210                              knows when a different control has been moved onto, and hence
02211                              that it should update the bubble help window.
02212                 Callback - the callback routine for ad-hoc bubble help - this is called to
02213                            get the bubble help text when the bubble window needs to be shown.
02214                 Reference - arbritrary data that the Callback may make use of to provide
02215                             it with context information for the callback event.
02216     Purpose:    Provide bubble-help on an ad-hoc basis, i.e. this allows bubble help to
02217                 be provided for GUI elements which are not necessarily 'controls' as such
02218                 on a Camelot bar.  For example the colour bar uses this to pretend that
02219                 each colour swatch is a control, and provides the bubble help to be 
02220                 shown on it.  This function should be called on mouse move messages over
02221                 such GUI elements. (It cannot be done automatically by the bubble help
02222                 system because we do not subclass such elements - because they're not
02223                 really individual windows!).
02224 
02225 ********************************************************************************************/
02226 
02227 void ControlHelper::DoBubbleHelpOn(wxWindow* Window, UINT32 NewControl, 
02228                                    BubbleHelpCallback Callback, void *Reference)
02229 {
02230     // Caller is telling us that they have detected a mouse move over one of their
02231     // controls - therefore take a record of their window handle, and fake an event
02232     // to the state machine.
02233     AdHocWindow = Window;
02234     AdHocCallback = Callback;
02235     AdHocReference = Reference;
02236 
02237     // Work out if this is a different control we're being told about.
02238     AdHocControlIsDifferent = (AdHocControl != NewControl);
02239 
02240     // Remember control ID for next time (we do this before calling BubbleHelpStateMachine()
02241     // because it uses AdHocControl to display the bubble help when it flips into the
02242     // Active state).
02243     AdHocControl = NewControl;
02244 
02245     // We give our WndProc to the state machine so that it thinks that NewControl is one of
02246     // our 'proper' controls.
02247     POINT Pos;
02248     ::GetCursorPos(&Pos);
02249     BubbleHelpStateMachine(Pos, Window, ControlHelper::MyWndProc);
02250 }
02251 
02252 
02253 /********************************************************************************************
02254 
02255 >   void ControlHelper::InformModalDialogOpened()
02256 
02257     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
02258     Created:    30/09/94
02259     Purpose:    Informs the ControlHelper class that a modal dialog which is NOT managed
02260                 by the kernel Dialog Manager (i.e. NOT a DialogOp) has been opened.
02261                 This allows us to disable bubble help whiole modal dialogs are open.
02262                 Examples of the use of this are the error box, or use of the Windows
02263                 Common Dialog functions.
02264     SeeAlso:    ControlHelper::DecModalDialogCount
02265 
02266 ********************************************************************************************/
02267 
02268 void ControlHelper::InformModalDialogOpened()
02269 {
02270     ModalDialogs++;
02271     // Record the currently active window
02272     DialogManager::RecordActiveDialogState();
02273     // This is the ideal place to disable all modeless dialogs
02274     DialogManager::EnableAllDialogs(FALSE);
02275 }
02276 
02277 
02278 /********************************************************************************************
02279 
02280 >   void ControlHelper::InformModalDialogClosed()
02281 
02282     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
02283     Created:    30/09/94
02284     Purpose:    Informs the ControlHelper class that a modal dialog which is NOT managed
02285                 by the kernel Dialog Manager (i.e. NOT a DialogOp) has been closed.
02286                 This allows us to disable bubble help whiole modal dialogs are open.
02287                 Examples of the use of this are the error box, or use of the Windows
02288                 Common Dialog functions.
02289     SeeAlso:    ControlHelper::IncModalDialogCount
02290 
02291 ********************************************************************************************/
02292 
02293 void ControlHelper::InformModalDialogClosed()
02294 {
02295     ModalDialogs--;
02296 
02297     // Restore the active window
02298     DialogManager::RestoreActiveDialogState();
02299 
02300     // Sanity check
02301     ENSURE(ModalDialogs >= 0, 
02302            "Unbalanced call to ControlHelper::InformModalDialogClosed()!");
02303 
02304 }
02305 
02306 
02307 /********************************************************************************************
02308 
02309 >   BOOL ControlHelper::IsUserInterfaceCaptured()
02310 
02311     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
02312     Created:    04/01/95
02313     Returns:    TRUE => User interface is captured in some way (e.g. a menu is up)
02314                 FALSE => User interface is not captured.
02315     Purpose:    Determine if the user interface is 'captured' in any way, e.g. if a
02316                 bar is being dragged or a menu is up, and so on. This is used to 
02317                 determine whether or not to do certain background/idle operations, e.g.
02318                 background rendering and the like.  This does NOT include drag operations
02319                 such as any tool drags etc., as this would mean that the push tool would
02320                 not work properly etc.
02321 
02322 ********************************************************************************************/
02323 
02324 BOOL ControlHelper::IsUserInterfaceCaptured()
02325 {
02326     // Bodge for delta - don't ever disable background rendering.
02327     return FALSE;
02328 
02329     if (Operation::GetCurrentDragOp() != NULL)
02330         // Camelot drag in progress - this does not count as capture.
02331         return FALSE;
02332 
02333     // Find out the handle of our main window
02334     CWnd *pMainCWnd = AfxGetMainWnd();
02335 
02336     wxWindow* MainwxWindow* = NULL;
02337 
02338     if (pMainCWnd != NULL)
02339         MainwxWindow* = pMainCWnd->GetSafewxWindow*();
02340 
02341     // Find out which window, if any, is captured.
02342     wxWindow* CapturedwxWindow* = ::GetCapture();
02343 
02344     // Return TRUE if mouse is captured, and it is not our window.
02345     return ((CapturedwxWindow* != NULL) && (CapturedwxWindow* != MainwxWindow*));
02346 }

Generated on Sat Nov 10 03:48:18 2007 for Camelot by  doxygen 1.4.4