customlist.cpp

Go to the documentation of this file.
00001 // $Id: customlist.cpp 1082 2006-05-16 14:42:54Z 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 
00100 // CustomList.cpp : implementation file
00101 //
00102 // Contains the classes that make up the custom list control. These are :
00103 //
00104 // CCustomList                  -   represents a custom list control to the 
00105 //                                  outside world. A calling class interacts with
00106 //                                  this class, not the helpers. (Hence perhaps
00107 //                                  the helper classes should be private nested?)
00108 // CCustomListScrollableArea    -   helper class containing the whole scrollable
00109 //                                  area, some of which may not be visible (ie.
00110 //                                  the verticle area is less than the area of
00111 //                                  the containing CCustomList
00112 // CCustomListRowWnd            -   helper class representing a list row
00113 //
00114 // Author:                      MarcP
00115 // Date:                        18/11/04
00116 
00117 #include "camtypes.h" 
00118 #include "customlist.h"
00119 
00120 #ifdef _DEBUG
00121 #define new DEBUG_NEW
00122 #undef THIS_FILE
00123 static char THIS_FILE[] = __FILE__;
00124 #endif
00125 
00129 //                                                                         //
00130 //                              CCustomList                                //
00131 //                                                                         //
00135 
00136 const INT32     CCustomList::MAXCOLUMNS = 8;
00137 const INT32     CCustomList::MAXROWS = 100;
00138 const INT32     CCustomList::FONTHEIGHT = 14;
00139 const INT32     CCustomList::ROWHEIGHT = 17 ;
00140 const INT32     CCustomList::COLOUR_PATCH_WIDTH  =  12 ;
00141 const INT32     CCustomList::COLOUR_PATCH_HEIGHT = 12 ;
00142 const CString   CCustomList::WNDCLASSNAME = "cc_CustomList";
00143 
00144 BEGIN_MESSAGE_MAP(CCustomList, CWnd)
00145     //{{AFX_MSG_MAP(CCustomList)
00146     ON_WM_CREATE()
00147     ON_WM_VSCROLL()
00148     ON_WM_SETFOCUS()
00149     ON_WM_MOUSEWHEEL()
00150     //}}AFX_MSG_MAP
00151 END_MESSAGE_MAP()
00152 
00153 // CCustomList constructor
00154 CCustomList::CCustomList() :
00155     m_VScrollBar(NULL),
00156     m_ScrollableArea(NULL)
00157 {
00158     // create column positioning data
00159     m_ColumnOffsetsArray = new INT32[MAXCOLUMNS];
00160     m_ColumnOffsetsArray[0] = 10; // indent for first offset
00161     for( INT32 i=1; i < MAXCOLUMNS ; i++)
00162         m_ColumnOffsetsArray[i] = -1;
00163 }
00164 
00165 // Intial window procedure for the custom control, specified in the WNDCLASS structure - see RegisterWindowClass.
00166 // Once registered Dialogs creating a cc_CustomList window will start through here. It is only called once per 
00167 // instance so once we hahave created the object we can then hand over to MFC to do manage the rest.
00168 extern "C" LRESULT CALLBACK EXPORT CCustomList::CustomWindowProc(HWND hWnd, UINT32 nMsg, WPARAM wParam, LPARAM lParam)
00169 {
00170     // Create CCustomList object and attach it to window
00171     CCustomList* pWnd = new CCustomList;
00172     pWnd->Attach(hWnd);
00173     // Switch over to MFC WndProc
00174     ::SetWindowLong(hWnd, GWL_WNDPROC, (DWORD)AfxWndProc);
00175     // and call it
00176     return ::CallWindowProc(AfxWndProc, hWnd, nMsg, wParam, lParam);
00177 }
00178 
00179 // register the the window class for this custom control. this must be executed before
00180 // invoking any dialogs that use the control, because it specifies the WndProc that 
00181 // they must use to create it
00182 BOOL CCustomList::RegisterWindowClass()
00183 {
00184     HINSTANCE hInst = AfxGetInstanceHandle();
00185     WNDCLASS wc;
00186     if (!(::GetClassInfo(hInst, WNDCLASSNAME, &wc)))
00187     {
00188         // Fill in the values for the class
00189         wc.style         = CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS ;
00190         wc.lpfnWndProc   = (WNDPROC) CCustomList::CustomWindowProc;
00191         wc.cbClsExtra       = wc.cbWndExtra = 0;
00192         wc.hInstance     = hInst;
00193         wc.hIcon         = NULL;
00194         wc.hCursor       = LoadCursor (NULL, _R(IDC_ARROW));
00195         wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
00196         wc.lpszMenuName  = (LPSTR) NULL;
00197         wc.lpszClassName = (LPCTSTR) WNDCLASSNAME;
00198 
00199         // try and register the class
00200         if (!RegisterClass (&wc))
00201         {
00202             ASSERT(FALSE);
00203             return FALSE;
00204         }
00205     }
00206     return TRUE;
00207 }
00208 
00209 CCustomList::~CCustomList()
00210 {
00211     // delete column offsets array
00212     delete m_ColumnOffsetsArray;
00213     // delete CScrollbar
00214     delete m_VScrollBar ;
00215 }
00216 
00217 // Called by the default OnNcDestroy member function after the window has been destroyed.
00218 void CCustomList::PostNcDestroy() 
00219 {
00220     delete this;
00221     CWnd::PostNcDestroy();
00222 }
00223 
00224 
00225 // Static function to retrieve a pointer to a custom list gadget knowing its resource ID and the parent's handle
00226 CCustomList* CCustomList::GetGadget(CWindowID hDlg, CGadgetID nIDDlgItem)
00227 {
00228     HWND listwnd = ::GetDlgItem(hDlg, nIDDlgItem);
00229     CCustomList* pListGadget = (CCustomList*)CWnd::FromHandlePermanent(listwnd);
00230     return pListGadget ;
00231 }
00232 
00233 
00234 // Adds an item to the list (pItemImage points to an optional bitmap associated with the item and displayed to its right
00235 BOOL CCustomList::AddItem(StringBase& itemString, KernelBitmap* pItemImage)
00236 {
00237     CCustomListRowWnd* pNewRow = GetScrollableArea()->AddRow();
00238     pNewRow ->AddCheck(0);
00239     pNewRow ->AddText(1,(TCHAR *)itemString);
00240     return TRUE;
00241 }
00242 
00243 // Adds a "references" item to the list
00244 BOOL CCustomList::AddRefsItem(UINT32 idStatusBitmap, StringBase& strItemName, StringBase& strDetails)
00245 {
00246     CCustomListRowWnd* pNewRow = GetScrollableArea()->AddRow();
00247     if (pNewRow)
00248     {
00249         if (idStatusBitmap!=0)
00250         {
00251             HBITMAP hBitmap = LoadBitmap(AfxGetResourceHandle(), MAKEINTRESOURCE(idStatusBitmap));
00252             pNewRow->AddBitmap(0, hBitmap, NULL, 0xFFFFFF); // We know background colour is white for these bitmaps!
00253         }
00254         pNewRow->AddText(1, (TCHAR*)strItemName);
00255         pNewRow->AddText(2, (TCHAR*)strDetails);
00256         return TRUE;
00257     }
00258 
00259     return FALSE;
00260 }
00261 
00262 // Retrieve the text of an item or one of its components
00263 BOOL CCustomList::GetItemString(StringBase& itemString, UINT32 itemIndex, UINT32 columnIndex) const
00264 {
00265     CString s = GetScrollableArea()->GetRow(itemIndex)->GetText(columnIndex);
00266     itemString.Empty(); // ensure buffer is empty
00267     itemString+= s;
00268     return TRUE;
00269 }
00270 
00271 // Retrieves the current state of a switch (ON/OFF)
00272 BOOL CCustomList::GetSwitchState(UINT32 itemIndex, UINT32 switchIndex) const
00273 {
00274     return GetScrollableArea()->GetRow(itemIndex)->IsChecked(switchIndex);
00275 }
00276 
00277 // Returns the number of items contained in the list
00278 INT32 CCustomList::GetItemCount() const
00279 {
00280     return GetScrollableArea()->m_RowCount;
00281 }
00282 
00283 // Returns the index of the item currently selected, -1 if none 
00284 INT32 CCustomList::GetSelectedItemIndex() const
00285 {
00286     return GetScrollableArea()->m_CurrentSelectedRow;
00287 }
00288 
00289 // Sets the state of a switch (determines whether the bitmapOn or bitmapOf is shown)
00290 BOOL CCustomList::SetSwitchState(BOOL state, UINT32 itemIndex, UINT32 switchIndex)
00291 {
00292     GetScrollableArea()->GetRow(itemIndex)->SetChecked(switchIndex,state);
00293     return TRUE;
00294 }
00295 
00296 // Adds an item to the list, creating the associated image from a bitmap resource ID
00297 BOOL CCustomList::AddItem(StringBase& itemString, UINT32 bitmapEnabledID, UINT32 bitmapDisabledID)
00298 {
00299     HBITMAP hItemBitmapEnabled = LoadBitmap (AfxGetResourceHandle(),MAKEINTRESOURCE(bitmapEnabledID));
00300     // the greyed bitmap feature is unimplemented for now
00301     //HBITMAP hItemBitmapDisabled = LoadBitmap (AfxGetResourceHandle(),MAKEINTRESOURCE(bitmapDisabledID));
00302 
00303     // note: RowWnd objects destroy any bitmaps on deletion 
00304     CCustomListRowWnd* pNewRow = GetScrollableArea()->AddRow();
00305     pNewRow ->AddCheck(0);
00306     pNewRow ->AddBitmap(1,hItemBitmapEnabled,NULL);
00307     pNewRow ->AddText(2,(TCHAR *)itemString);
00308 
00309     return TRUE;
00310 }
00311 
00312 // Sets the given item to be selected (and deselects all other items)
00313 void CCustomList::SetSelectedItemIndex(INT32 NewSel)
00314 {
00315     GetScrollableArea()->SelectRow(NewSel);
00316 }
00317 
00318 // deletes the list by deleting the scrollable area and replacing it with a new one
00319 BOOL CCustomList::DeleteAllItems()
00320 {
00321     if(m_ScrollableArea)
00322     {
00323         m_ScrollableArea->DestroyWindow();
00324         //delete m_ScrollableArea;
00325     }
00326     NewScrollableArea();
00327     return TRUE;
00328 }
00329 
00330 // creates a new scrollable area
00331 void CCustomList::NewScrollableArea()
00332 {
00333     m_ScrollableArea = new CCustomListScrollableArea(this);
00334     BOOL b = m_ScrollableArea->Create(_T("STATIC"), NULL, WS_CHILD | WS_VISIBLE,CRect(0,0,0,0),this,0,NULL);
00335 }
00336 
00337 // called on creation. creates the v scroller and a new scrollable area
00338 INT32 CCustomList::OnCreate(LPCREATESTRUCT lpCreateStruct) 
00339 {
00340     if (CWnd::OnCreate(lpCreateStruct) == -1)
00341         return -1;
00342 
00343     m_VScrollBar = new CScrollBar();
00344     CRect parentRect;
00345     CRect rect;
00346     GetClientRect(&parentRect);
00347     rect.left = parentRect.right - ::GetSystemMetrics(SM_CXVSCROLL) ;
00348     rect.top = 0;
00349     rect.right = parentRect.right ;
00350     rect.bottom = parentRect.bottom ;
00351     m_VScrollBar->Create(SBS_VERT, rect, this, NULL);
00352     m_VScrollBar->ShowWindow(SW_SHOW);
00353 
00354     NewScrollableArea();
00355 
00356     return 0;
00357 }
00358 
00359 // handle a scroll message
00360 void CCustomList::OnVScroll(UINT32 nSBCode, UINT32 nPos, CScrollBar* pScrollBar) 
00361 {
00362     m_ScrollableArea->HandleScrollMessage(nSBCode, nPos) ;
00363     CWnd::OnVScroll(nSBCode, nPos, pScrollBar);
00364 }
00365 
00366 // helper function - creates a bitmap of the specified width and height
00367 HBITMAP CreateScreenCompatibleBitmap( INT32 width, INT32 height)
00368 {
00369     HDC hProbeDC = ::CreateCompatibleDC(NULL); // device context used to probe the current screen mode
00370     ERROR3IF(hProbeDC == NULL, "Couldn't create probe DC");
00371     const BITMAP bitmapData = 
00372     { 
00373         0,
00374         width,
00375         height,
00376         width * GetDeviceCaps(hProbeDC, BITSPIXEL) * GetDeviceCaps(hProbeDC, PLANES) / 8 ,
00377         GetDeviceCaps(hProbeDC, PLANES),
00378         GetDeviceCaps(hProbeDC, BITSPIXEL),
00379         0L
00380     };
00381     DeleteDC(hProbeDC);
00382     return CreateBitmapIndirect(&bitmapData);
00383 }
00384 
00385 // Used if the the listitem is a actually a colour - it displays a square colour patch next to the color name
00386 BOOL CCustomList::AddColourListItem(StringBase& colourName, INT32 red, INT32 green, INT32 blue, BOOL IsSpotColour)
00387 {
00388     //First create and insert the colour patch associated with the item
00389     //Create a memory DC and 2 bitmaps (enbled/disabled) compatible with the screen 
00390     HDC hDC = ::CreateCompatibleDC(NULL);
00391     ERROR3IF(hDC == NULL, "Couldn't create rendering DC");
00392 
00393     // do not create grey bitmaps for now (to add back in extend array to 2 and comment
00394     // below back in)
00395     HBITMAP bitmaps[1];
00396     bitmaps[0] = CreateScreenCompatibleBitmap(COLOUR_PATCH_WIDTH,   COLOUR_PATCH_HEIGHT);
00397     //bitmaps[1] = CreateScreenCompatibleBitmap(COLOUR_PATCH_WIDTH, COLOUR_PATCH_HEIGHT);
00398     //ERROR2IF(!(bitmaps[0] && bitmaps[1]), FALSE, "GDI Error");
00399 
00400     // And draw the colour patch into the bitmap
00401     for (INT32 i = 0; i <= 1; i++)
00402     {
00403         if (hDC != NULL)
00404         {
00405             HBITMAP OldBitmap = (HBITMAP) ::SelectObject(hDC, bitmaps[i]);
00406 
00407             COLORREF Colour = RGB(red, green, blue);
00408             if (i != 0)
00409             {
00410                 // Convert the colour to a greyscale
00411                 BYTE Grey = BYTE((red * 0.305) + (green * 0.586) + (blue * 0.109));
00412                 Colour = RGB(Grey, Grey, Grey);
00413             }
00414 
00415             HBRUSH Brush = ::CreateSolidBrush(Colour);
00416 
00417             ERROR3IF(Brush == NULL, "Couldn't create brush");
00418 
00419             HPEN BlackPen = (HPEN) ::GetStockObject(BLACK_PEN);
00420 
00421             HBRUSH WhiteBrush   = (HBRUSH) ::GetStockObject(WHITE_BRUSH);
00422             HPEN NullPen        = (HPEN) ::GetStockObject(NULL_PEN);
00423             HPEN OldPen         = (HPEN) ::SelectObject(hDC, NullPen);
00424             HBRUSH OldBrush     = (HBRUSH) ::SelectObject(hDC, WhiteBrush);
00425             if (IsSpotColour)
00426             {
00427                 // Spot colours are drawn as circles - but first, fill the bitmap with white,
00428                 // so that the un-covered corners of the square are a sensible colour.
00429 
00430                 ::Rectangle(hDC, 0, 0, COLOUR_PATCH_WIDTH+1, COLOUR_PATCH_HEIGHT+1);
00431                 ::SelectObject(hDC, BlackPen);
00432                 ::SelectObject(hDC, Brush);
00433                 ::Ellipse(hDC, 0, 0, COLOUR_PATCH_WIDTH, COLOUR_PATCH_HEIGHT);
00434 
00435             }
00436             else
00437             {
00438                 // Process colours are shown as rectangles
00439 
00440                 ::Rectangle(hDC, 0, 0, COLOUR_PATCH_WIDTH+1, COLOUR_PATCH_HEIGHT+1);
00441                 ::SelectObject(hDC, BlackPen);
00442                 ::SelectObject(hDC, Brush);
00443                 ::Rectangle(hDC, 0, 0, COLOUR_PATCH_WIDTH-1, COLOUR_PATCH_HEIGHT-1);
00444             
00445             }
00446 
00447             ::SelectObject(hDC, OldPen);
00448             ::SelectObject(hDC, OldBrush);
00449             ::SelectObject(hDC, OldBitmap);
00450 
00451             ::DeleteObject(Brush);
00452         }
00453     }
00454 
00455     DeleteDC(hDC);
00456 //  this->AddItem(colourName,bitmaps[0],bitmaps[1]);
00457     CCustomListRowWnd* pNewRow = GetScrollableArea()->AddRow();
00458     pNewRow ->AddCheck(0);
00459     pNewRow ->AddCheck(1);
00460     pNewRow ->AddBitmap(2,bitmaps[0],NULL);
00461     pNewRow ->AddText(3,(TCHAR*)colourName);
00462 
00463     return TRUE;
00464 }
00465 
00466 // Sets the text for an item or one of its components
00467 BOOL CCustomList::SetItemString(StringBase& itemString, UINT32 itemIndex, UINT32 columnIndex)
00468 {
00469     GetScrollableArea()->GetRow(itemIndex)->SetText(columnIndex,(TCHAR*)  itemString);
00470     return TRUE;
00471 }
00472 
00473 // helper function to loop through and enable / disable all child windows
00474 void EnableDescendants(HWND hWnd, BOOL enable)
00475 {
00476     // walk through HWNDs to avoid creating temporary CWnd objects
00477     // unless we need to call this function recursively
00478     for (HWND hWndChild = ::GetTopWindow(hWnd); hWndChild != NULL;
00479         hWndChild = ::GetNextWindow(hWndChild, GW_HWNDNEXT))
00480     {
00481         CWnd* pWnd = CWnd::FromHandlePermanent(hWndChild);
00482         if (pWnd != NULL)
00483         {
00484             pWnd->EnableWindow(enable);
00485             pWnd->RedrawWindow();
00486         }
00487         if (::GetTopWindow(hWndChild) != NULL)
00488         {
00489             // send to child windows after parent
00490             EnableDescendants(hWndChild,enable);
00491         }
00492     }
00493 }
00494 
00495 // We are using our own enabler function as the Windows one looks awful (called by the DialogManager)
00496 // DO NOT call DialogManager::EnableGadget on a control before you have created it!
00497 BOOL CCustomList::SetEnabled(BOOL enabled)
00498 {
00499     GetScrollableArea()->SelectRow(-1); // deselect any selected row 
00500     EnableWindow(enabled);
00501     EnableDescendants(m_hWnd,enabled);
00502     return TRUE;
00503 }
00504 
00505 // sets the offset of the numbered column by adding the specified width to preceding offsets
00506 void    CCustomList::SetColumnWidth(INT32 colnum,INT32 colwidth)
00507 {
00508     ASSERT(colnum + 1 < MAXCOLUMNS);
00509 
00510     // set at colnum+1
00512     ERROR3IF(m_ColumnOffsetsArray[colnum]==-1,"SetColumnWidth - illegal column offset. ensure preceding column widths are set");
00513     m_ColumnOffsetsArray[colnum+1] = m_ColumnOffsetsArray[colnum] + colwidth;
00514 }
00515 
00516 // helper function to change the background colour of the bitmap to the one used by dialog backgrounds
00517 BOOL SetBitmapBkgToSystem(HBITMAP hBitmap)
00518 {
00519     BITMAP bitmap;
00520     HDC hBitmapDC = CreateCompatibleDC(NULL);
00521     if (!GetObject(hBitmap, sizeof(bitmap), &bitmap) || !hBitmapDC)
00522     {
00523         ERROR2RAW("Non-fatal GDI error");
00524         return(FALSE);
00525     }
00526     SelectObject(hBitmapDC, hBitmap);
00527     // We make the assumption that the pixel in the lower right corner has the background colour 
00528     DWORD currentBkColour = (DWORD) GetPixel(hBitmapDC, bitmap.bmWidth - 1, bitmap.bmHeight -1); 
00529     DWORD sysBkColour = GetSysColor(COLOR_3DFACE);
00530     for (INT32 i = 0; i < bitmap.bmWidth; i++)
00531     {
00532         for (INT32 j = 0; j < bitmap.bmHeight; j++)
00533         {
00534             if ((DWORD) GetPixel(hBitmapDC, i, j) == currentBkColour)
00535                 SetPixelV(hBitmapDC, i, j, (COLORREF) sysBkColour);
00536         }
00537     }
00538     DeleteDC(hBitmapDC);
00539     return TRUE;
00540 }
00541 
00542 // Function to create a custom header from a bitmap resource
00543 BOOL CCustomList::CreateCustomHeader(UINT32 bitmapID)
00544 {
00545     //The listview control has a built-in header, but we will create our own as we want it to display bitmaps
00546     //First get control's coordinates, so that we know where to place the header
00547 
00548     CRect listviewRect;
00549     GetWindowRect(&listviewRect);
00550     POINT listviewOrigin = { listviewRect.left, listviewRect.top };
00551     ::ScreenToClient((HWND) GetOwner()->m_hWnd, &listviewOrigin);
00552 
00553     if(!m_hHeaderBitmap.LoadBitmap(MAKEINTRESOURCE(bitmapID)))
00554         ERROR2RAW("Failed to load header bitmap");
00555 
00556     //Get the height of the bitmap so we can figure out the height of the header
00557     BITMAP bitmap ;
00558     if (!m_hHeaderBitmap.GetBitmap(&bitmap))
00559     {
00560         ERROR2RAW("Failed to get header bitmap data");
00561         return(FALSE);
00562     }
00563     //Change the background colour of the bitmap to the one used by dialog backgrounds, in case the colour scheme used is not the default 
00564     SetBitmapBkgToSystem(m_hHeaderBitmap);
00565     
00566     CRect srect;
00567     srect.left = listviewOrigin.x + m_ColumnOffsetsArray[0] + 1;
00568     srect.right = listviewRect.right  ;
00569     srect.top   = listviewOrigin.y - bitmap.bmHeight;
00570     srect.bottom = listviewOrigin.y;
00571 
00572     m_hHeader.Create(NULL, WS_VISIBLE | SS_BITMAP, srect, this->GetOwner());
00573     m_hHeader.SetBitmap(m_hHeaderBitmap);
00574     return TRUE;
00575 }
00576 
00577 
00578 // called when the custom control receives the focus. ensure we are sensibly
00579 // initialised before actually doing anything
00580 void CCustomList::OnSetFocus(CWnd* pOldWnd) 
00581 {
00582     CWnd::OnSetFocus(pOldWnd);
00583     
00584     if(GetScrollableArea()->GetSafeHwnd()!=NULL)
00585     {
00586         if(GetScrollableArea()->m_RowCount > 0 && GetScrollableArea()->m_ListRowsArray)
00587         {
00588             INT32 Sel = GetScrollableArea()->m_CurrentSelectedRow;
00589             if(Sel==-1 || GetScrollableArea()->m_CurrentSelectedRow > GetScrollableArea()->m_RowCount)
00590                 Sel=0;
00591             GetScrollableArea()->SelectRow(Sel);
00592         }
00593     }
00594     
00595 }
00596 
00600 //                                                                         //
00601 //                  CCustomListScrollableArea                              //
00602 //                                                                         //
00606 
00607 BEGIN_MESSAGE_MAP(CCustomListScrollableArea, CWnd)
00608     //{{AFX_MSG_MAP(CCustomListScrollableArea)
00609     ON_WM_SIZE()
00610     //}}AFX_MSG_MAP
00611 END_MESSAGE_MAP()
00612 
00613 // CCustomListScrollableArea constructor
00614 CCustomListScrollableArea::CCustomListScrollableArea(CCustomList* parent) :
00615     m_CurrentSelectedRow(-1),
00616     m_RowCount(0),
00617     m_Parent(parent),
00618     m_ListRowsArray(NULL),
00619     m_ScrollPos(0)
00620 {
00621     // create font for text items
00622     LOGFONT lf;                        // Used to create the CFont.
00623     memset(&lf, 0, sizeof(LOGFONT));   // Clear out structure.
00624     lf.lfHeight = CCustomList::FONTHEIGHT;
00625     strcpy(lf.lfFaceName, "Microsoft Sans Serif");    //    with face name "Arial".
00626     m_Font.CreateFontIndirect(&lf);    // Create the font.
00627 
00628     // create the row objects
00629     m_ListRowsArray = new CCustomListRowWnd*[CCustomList::MAXROWS];
00630     for(INT32 i=0; i<CCustomList::MAXROWS; i++)
00631     {
00632         m_ListRowsArray[i] = NULL;
00633     }
00634 
00635     //  set up window class for the rows
00636     WNDCLASS NewWindowClass;
00637     memset(&NewWindowClass, 0, sizeof(WNDCLASS));
00638     NewWindowClass.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS ;
00639     NewWindowClass.lpfnWndProc = ::DefWindowProc;
00640     NewWindowClass.hInstance = AfxGetInstanceHandle();
00641     NewWindowClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); ;//(HBRUSH) ((COLOR_BTNFACE));
00642     NewWindowClass.lpszMenuName = NULL;
00643     NewWindowClass.lpszClassName = _T("RowClass");
00644     AfxRegisterClass(&NewWindowClass);
00645 }
00646 
00647 // CCustomListScrollableArea destructor
00648 CCustomListScrollableArea::~CCustomListScrollableArea()
00649 {
00650     delete m_ListRowsArray;
00651     m_ListRowsArray = NULL;
00652 }
00653 
00654 // Called after the window has been destroyed.
00655 void CCustomListScrollableArea::PostNcDestroy() 
00656 {
00657     delete this;
00658     CWnd::PostNcDestroy();
00659 }
00660 
00661 
00662 // respond to a scrollbar event by scrolling this window up or down
00663 void CCustomListScrollableArea::HandleScrollMessage(UINT32 nSBCode, UINT32 nPos )
00664 {
00665     SCROLLINFO si;
00666     si.cbSize = sizeof(SCROLLINFO);
00667     si.fMask = SIF_TRACKPOS|SIF_PAGE|SIF_RANGE;
00668     m_Parent->GetVScrollBar()->GetScrollInfo(&si);
00669 
00670     INT32 NewPos = m_ScrollPos ;
00671     switch(nSBCode)
00672     {
00673     case SB_PAGEDOWN:
00674         NewPos = m_ScrollPos + si.nPage;
00675         break ;
00676     case SB_PAGEUP:
00677         NewPos = m_ScrollPos - si.nPage;
00678         break ;
00679     case SB_LINEDOWN:
00680         NewPos = m_ScrollPos + CCustomList::ROWHEIGHT;
00681         break ;
00682     case SB_LINEUP:
00683         NewPos = m_ScrollPos - CCustomList::ROWHEIGHT;
00684         break ;
00685     case SB_THUMBTRACK:
00686     case SB_THUMBPOSITION:
00687         NewPos = nPos;
00688         break;
00689     }
00690     INT32 LastPos = si.nMax-si.nPage + 1 ; 
00691 
00692     if( NewPos < 0 )
00693         NewPos = 0;
00694     if( NewPos > LastPos )
00695         NewPos = LastPos ;
00696 
00697     INT32 offset = m_ScrollPos - NewPos;
00698     this->ScrollWindow(0,offset);
00699     m_ScrollPos = NewPos;
00700     m_Parent->GetVScrollBar()->SetScrollPos(m_ScrollPos , true);
00701 }
00702 
00703 // return a pointer to the specified row object
00704 CCustomListRowWnd* CCustomListScrollableArea::GetRow(INT32 row)
00705 {
00706     ASSERT(row < m_RowCount);
00707     return m_ListRowsArray[row] ;
00708 }
00709 
00710 
00711 // select the specified row, scrolling if necessary so that 
00712 // the selection is in view. a value of -1 deselects any 
00713 // selection
00714 
00715 void CCustomListScrollableArea::SelectRow(INT32 RowNum)
00716 {
00717     if( m_CurrentSelectedRow > -1 )
00718     {
00719         m_ListRowsArray[m_CurrentSelectedRow]->m_Selected = false ;
00720         m_ListRowsArray[m_CurrentSelectedRow]->RedrawWindow();
00721     }
00722     m_CurrentSelectedRow = RowNum;
00723 
00724     if( m_CurrentSelectedRow > -1 && this->IsWindowEnabled())
00725     {
00726         // scroll if necessary so that the selection is in view
00727         CRect parentRect;
00728         m_Parent->GetClientRect(&parentRect);
00729         INT32 Page = parentRect.Height();
00730         if((m_CurrentSelectedRow) * CCustomList::ROWHEIGHT < m_ScrollPos)
00731         {
00732             HandleScrollMessage(SB_THUMBPOSITION,m_CurrentSelectedRow * CCustomList::ROWHEIGHT);
00733         }
00734         else if((m_CurrentSelectedRow+1) * CCustomList::ROWHEIGHT > (m_ScrollPos + Page))
00735         {
00736             HandleScrollMessage(SB_THUMBPOSITION,(m_CurrentSelectedRow+1) * CCustomList::ROWHEIGHT - Page);
00737         }
00738 
00739         ASSERT(m_ListRowsArray!=NULL);
00740         m_ListRowsArray[m_CurrentSelectedRow]->m_Selected = true ;
00741         m_ListRowsArray[m_CurrentSelectedRow]->RedrawWindow();
00742         m_ListRowsArray[m_CurrentSelectedRow]->SetFocus();
00743     }
00744 }
00745 
00746 // the size has changed so adjust the scrollbar to reflect the new scrollable area size
00747 void CCustomListScrollableArea::OnSize(UINT32 nType, INT32 cx, INT32 cy) 
00748 {
00749     ASSERT( GetHeight() == cy );
00750     CWnd::OnSize(nType, cx, cy);
00751     
00752     CRect parentRect;
00753     m_Parent->GetClientRect(parentRect);
00754 
00755     // scrollbar window may not actually be visible
00756     SCROLLINFO si;
00757     si.cbSize = sizeof(SCROLLINFO);
00758     si.fMask = SIF_PAGE|SIF_RANGE;
00759     si.nPage = (INT32)parentRect.Height();
00760     si.nMin = 0;     
00761     si.nMax = GetHeight(); 
00762     m_Parent->m_VScrollBar->SetScrollInfo(&si, true);
00763 }
00764 
00765 
00769 //                                                                         //
00770 //                          CCustomListRowWnd                              //
00771 //                                                                         //
00775 
00776 BEGIN_MESSAGE_MAP(CCustomListRowWnd, CWnd)
00777     //{{AFX_MSG_MAP(CCustomListRowWnd)
00778     ON_WM_LBUTTONUP()
00779     ON_WM_ERASEBKGND()
00780     ON_WM_CTLCOLOR()
00781     ON_WM_LBUTTONDBLCLK()
00782     ON_WM_KEYDOWN()
00783     ON_WM_GETDLGCODE()
00784     ON_WM_DESTROY()
00785     //}}AFX_MSG_MAP
00786 END_MESSAGE_MAP()
00787 
00788 CCustomListRowWnd::CCustomListRowWnd(INT32 RowNo, CCustomListScrollableArea* parent) :
00789     m_BackBrush(RGB(255,255,255)) ,
00790     m_BackBrushSel(::GetSysColor (COLOR_HIGHLIGHT)),
00791     m_Selected(false),
00792     m_RowNum(RowNo),
00793     m_Parent(parent)
00794 {
00795 
00796     // column object storage
00797     m_ColumnObjects = new CObject* [CCustomList::MAXCOLUMNS];
00798     for( INT32 i=0; i < CCustomList::MAXCOLUMNS; i++)
00799         m_ColumnObjects[i] = NULL;
00800 }
00801 
00802 CCustomListRowWnd::~CCustomListRowWnd()
00803 {
00804     // clear down column object storage
00805     for( INT32 i=0; i < CCustomList::MAXCOLUMNS; i++)
00806     {
00807         if ( m_ColumnObjects[i] !=NULL )
00808         {
00809             delete m_ColumnObjects[i];
00810             m_ColumnObjects[i] = NULL;
00811         }
00812     }
00813     delete m_ColumnObjects ;
00814 }
00815 
00816 //Called after the window has been destroyed.
00817 void CCustomListRowWnd::PostNcDestroy() 
00818 {
00819     delete this;
00820     CWnd::PostNcDestroy();
00821 }
00822 
00823 // Ensure that any bitmap handles are deleted. We do it here because we need
00824 // the controls that store them to still be "alive"
00825 void CCustomListRowWnd::OnDestroy() 
00826 {
00827     CWnd::OnDestroy();
00828     for( INT32 i=0; i < CCustomList::MAXCOLUMNS; i++)
00829     {
00830         if ( m_ColumnObjects[i] !=NULL )
00831         {
00832             if( m_ColumnObjects[i]->IsKindOf(RUNTIME_CLASS(CStatic)) )
00833             {
00834                 if( ((CStatic*)m_ColumnObjects[i])->GetSafeHwnd() )
00835                 {
00836                     HBITMAP hBitmap = ((CStatic*)m_ColumnObjects[i])->GetBitmap();
00837                     if(hBitmap)
00838                     {
00839                         ::DeleteObject(hBitmap);
00840                     }
00841                 }
00842             }
00843         }
00844     }
00845 }
00846 
00847 // add a check box at the specified column
00848 void CCustomListRowWnd::AddCheck(INT32 col)
00849 {
00850     CButton* pBut = new CButton();
00851     m_ColumnObjects[col] = pBut ;
00852 
00853     CRect cr;
00854     GetClientRect(&cr);
00855     ASSERT(cr.Height() == CCustomList::ROWHEIGHT);
00856 
00857     CRect rect;
00858     rect.left   =   m_Parent->m_Parent->m_ColumnOffsetsArray[col] ;
00859     rect.right  =   rect.left + GetSystemMetrics(SM_CXMENUCHECK);
00860     rect.top    = (CCustomList::ROWHEIGHT - GetSystemMetrics(SM_CYMENUCHECK))/2;
00861     rect.bottom = rect.top + GetSystemMetrics(SM_CYMENUCHECK)  ;
00862     pBut->Create(NULL, WS_CHILD|WS_VISIBLE|BS_AUTOCHECKBOX, rect, this, 4);
00863 }
00864 
00865 // set the checkbox at the specified  column
00866 void CCustomListRowWnd::SetChecked(INT32 col, BOOL checked)
00867 {
00868     ASSERT(m_ColumnObjects[col] != NULL );
00869     ASSERT(m_ColumnObjects[col]->IsKindOf(RUNTIME_CLASS( CButton)));
00870 
00871     CButton* pBut = (CButton*)m_ColumnObjects[col];
00872     pBut->SetCheck(checked ? BST_CHECKED : BST_UNCHECKED );
00873     pBut->UpdateWindow();
00874 }
00875 
00876 // test the switch value at the specified  column
00877 bool CCustomListRowWnd::IsChecked(INT32 col) const
00878 {
00879     CButton* pBut = (CButton*)m_ColumnObjects[col];
00880     return pBut->GetCheck() == BST_CHECKED;
00881 }
00882 
00883 
00884 // we have been clicked - but let the parent class manage it
00885 void CCustomListRowWnd::OnLButtonUp(UINT32 nFlags, CPoint point) 
00886 {
00887     CWnd::OnLButtonUp(nFlags, point);
00888     m_Parent->SelectRow(m_RowNum);
00889     CWnd::OnLButtonUp(nFlags, point);
00890 }
00891 
00892 // called when object background needs erasing (for example, when resized). 
00893 BOOL CCustomListRowWnd::OnEraseBkgnd(CDC* pDC) 
00894 {
00895     CBrush* pOldBrush = pDC->SelectObject(m_Selected ? &m_BackBrushSel : &m_BackBrush);
00896 
00897     CRect rect;
00898     pDC->GetClipBox(&rect);     // Erase the area needed
00899 
00900     pDC->PatBlt(rect.left,rect.top,rect.Width(),rect.Height(),PATCOPY);
00901     pDC->SelectObject(pOldBrush);
00902 
00903     return true;
00904 }
00905 
00906 // add text to the specified column of this row
00907 void CCustomListRowWnd::AddText(INT32 col, CString text)
00908 {
00909     ASSERT(m_ColumnObjects[col] == NULL);
00910 
00911     CStatic* pStat = new CStatic();
00912     m_ColumnObjects[col] = pStat ;
00913     
00914     CRect cr;
00915     GetClientRect(&cr);
00916 
00917     CRect srect;
00918     srect.left= m_Parent->m_Parent->m_ColumnOffsetsArray[col] ;
00919     if( col < CCustomList::MAXCOLUMNS && m_Parent->m_Parent->m_ColumnOffsetsArray[col + 1] > 0)
00920         srect.right= m_Parent->m_Parent->m_ColumnOffsetsArray[col + 1] ;
00921     else
00922         srect.right= cr.right;
00923     srect.top   = (CCustomList::ROWHEIGHT - CCustomList::FONTHEIGHT)/2;
00924     srect.bottom = srect.top + CCustomList::FONTHEIGHT;
00925     pStat->Create(text, WS_CHILD|WS_VISIBLE|WS_EX_LEFT, srect, this);
00926     pStat->SetFont(&m_Parent->m_Font);
00927 }
00928 
00929 // set the text at the specified column of this row
00930 void CCustomListRowWnd::SetText(INT32 col, CString text)
00931 {
00932     CStatic* pStat = (CStatic*)m_ColumnObjects[col];
00933     if( pStat == NULL )
00934         AddText(col,text);
00935     else
00936     {
00937         pStat->SetWindowText(text);
00938         pStat->UpdateWindow();
00939     }
00940 }
00941 
00942 // add bitmap at the specified  column
00943 void CCustomListRowWnd::AddBitmap(INT32 col, HBITMAP hBitmap,HBITMAP, DWORD dwBackColour)
00944 {
00945     CStatic* pStat = new CStatic();
00946     ASSERT(m_ColumnObjects[col] == NULL);
00947     m_ColumnObjects[col] = pStat ;
00948 
00949     CRect cr;
00950     GetClientRect(&cr);
00951 
00952     BITMAP bitmap;
00953     CBitmap::FromHandle(hBitmap)->GetBitmap(&bitmap);
00954     CRect srect;
00955     srect.left = m_Parent->m_Parent->m_ColumnOffsetsArray[col] ;
00956     srect.right = srect.left + bitmap.bmWidth;
00957     srect.top   = cr.Height()/2 - bitmap.bmHeight/2;
00958     srect.bottom = cr.Height()/2 + bitmap.bmHeight/2;
00959 
00961     HDC hBitmapDC = CreateCompatibleDC(NULL);
00962     if (!hBitmapDC)
00963     {
00964         ERROR2RAW("Non-fatal GDI error");
00965     }
00966     SelectObject(hBitmapDC, hBitmap);
00967     // Iff we haven't been told what the background colour is...
00968     // We make the assumption that the pixel in the lower right corner has the background colour
00969     if (dwBackColour == 0xFFFFFFFF)
00970         dwBackColour = (DWORD) GetPixel(hBitmapDC, bitmap.bmWidth - 1, bitmap.bmHeight -1); 
00971     DWORD sysBkColour = GetSysColor(COLOR_3DFACE);
00972     for (INT32 i = 0; i < bitmap.bmWidth; i++)
00973     {
00974         for (INT32 j = 0; j < bitmap.bmHeight; j++)
00975         {
00976             if ((DWORD) GetPixel(hBitmapDC, i, j) == dwBackColour)
00977                 SetPixelV(hBitmapDC, i, j, (COLORREF) sysBkColour);
00978         }
00979     }
00980     DeleteDC(hBitmapDC);
00981 
00983     pStat->Create(NULL, WS_VISIBLE | SS_BITMAP, srect, this);
00984     pStat->SetBitmap(hBitmap);
00985 }
00986 
00987 // invoked when child control is about to be drawn. prepare the pDC for drawing the 
00988 // control using the correct system colors.
00989 HBRUSH CCustomListRowWnd::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT32 nCtlColor) 
00990 {
00991     HBRUSH hbr = CWnd::OnCtlColor(pDC, pWnd, nCtlColor);
00992     COLORREF text = m_Selected ? GetSysColor(COLOR_HIGHLIGHTTEXT) : GetSysColor(COLOR_WINDOWTEXT);
00993     pDC->SetTextColor(text);
00994     pDC->SetBkMode(TRANSPARENT);
00995 
00996     return GetBackgroundBrush();
00997 }
00998 
00999 // The row has been double-clicked. Notify the control calling window
01000 // (usually a dialog) 
01001 void CCustomListRowWnd::OnLButtonDblClk(UINT32 nFlags, CPoint point) 
01002 {
01003     m_Parent->SelectRow(m_RowNum); // make sure we are selected
01004     CWnd* caller = m_Parent->m_Parent->GetOwner();
01005     INT32 ControlID = m_Parent->m_Parent->GetDlgCtrlID();
01006     if(caller && ControlID )
01007     {
01008         NMHDR nm;
01009         nm.hwndFrom = m_Parent->m_Parent->m_hWnd;
01010         nm.idFrom = ControlID;
01011         nm.code = NM_DBLCLK;
01012 
01013         ::SendMessage(caller->m_hWnd ,WM_NOTIFY,ControlID , (LPARAM)&nm );
01014     }
01015 }
01016 
01017 // return the text at the specified column
01018 CString CCustomListRowWnd::GetText(INT32 col) const
01019 {
01020     CStatic* pStat = (CStatic*)m_ColumnObjects[col];
01021     CString ret ;
01022     pStat->GetWindowText(ret);
01023     return ret;
01024 }
01025 
01026 // if up or down has been clicked, pass it on to the scrolling code
01027 void CCustomListRowWnd::OnKeyDown(UINT32 nChar, UINT32 nRepCnt, UINT32 nFlags) 
01028 {
01029     if(nChar == CAMKEY(DOWN) && m_RowNum + 1 < m_Parent->m_RowCount)
01030         m_Parent->SelectRow(m_RowNum+1);
01031     else if(nChar == CAMKEY(UP) && m_RowNum - 1 >= 0)
01032         m_Parent->SelectRow(m_RowNum-1);
01033 
01034     CWnd::OnKeyDown(nChar, nRepCnt, nFlags);
01035 }
01036 
01037 //  Make sure we receive UP/DOWN arrow key message (otherwise dialogs swallow them)
01038 UINT32 CCustomListRowWnd::OnGetDlgCode() 
01039 {
01040     UINT32 uRet = CWnd::OnGetDlgCode();
01041     uRet |= DLGC_WANTARROWS;
01042     return uRet;
01043 }
01044 
01045 // The framework calls this member function when a child control sends a notification message, 
01046 // or when an accelerator keystroke is translated. 
01047 BOOL CCustomListRowWnd::OnCommand(WPARAM wParam, LPARAM lParam) 
01048 {
01049     UINT32 nID = LOWORD(wParam);
01050     HWND hWndCtrl = (HWND)lParam;
01051     INT32 nCode = HIWORD(wParam);
01053     m_Parent->SelectRow(m_RowNum); // make sure we are selected
01054     CWnd* caller = m_Parent->m_Parent->GetOwner();
01055     INT32 ControlID = m_Parent->m_Parent->GetDlgCtrlID();
01056     if(caller && ControlID )
01057     {
01058         NMHDR nm;
01059         nm.hwndFrom = m_hWnd;
01060         nm.idFrom = ControlID;
01061         nm.code = NULL;
01062 
01063         ::SendMessage(caller->m_hWnd ,WM_NOTIFY, ControlID , (LPARAM)&nm);
01064     }
01065     return CWnd::OnCommand(wParam, lParam);
01066 }
01067 
01068 // adds a row window object to the scrollable area. Note the resultant
01069 // height change triggers a WM_SIZE message (handled by OnSize()).
01070 // Returns a pointer to the row window
01071 CCustomListRowWnd* CCustomListScrollableArea::AddRow()
01072 {
01073     CCustomListRowWnd* pRow = new CCustomListRowWnd(m_RowCount,this);
01074     ASSERT(pRow);
01075     m_ListRowsArray[m_RowCount] = pRow;
01076     m_RowCount++;
01077 
01078     CRect parentRect;
01079     m_Parent->GetClientRect(&parentRect);
01080 
01081     CRect rect ;
01082     rect.top = (m_RowCount-1) * CCustomList::ROWHEIGHT ;
01083     rect.left = 0 ;
01084     rect.bottom = 0 + (m_RowCount) * CCustomList::ROWHEIGHT ;
01085     rect.right = parentRect.right - 1;
01086 
01087     pRow->Create("RowClass","",WS_CHILD,rect,this,0);
01088     pRow->ShowWindow(SW_SHOW);
01089 
01090     // recalculate height and scrollbar position
01091     BOOL needScroll = GetHeight() > parentRect.Height();
01092     INT32 width = parentRect.right;
01093     if(needScroll)
01094         width=width - ::GetSystemMetrics(SM_CXVSCROLL); //+2
01095     m_Parent->m_VScrollBar->ShowWindow(needScroll ? SW_SHOW : SW_HIDE);
01096     SetWindowPos(NULL,0,0,width,GetHeight(),0);
01097 
01098     return pRow;
01099 }
01100 
01101 
01102 
01103 // respond to mousewheel events by paging or scrolling accordingly
01104 BOOL CCustomList::OnMouseWheel(UINT32 nFlags, short zDelta, CPoint pt) 
01105 {
01106     SCROLLINFO si;
01107     si.cbSize = sizeof(SCROLLINFO);
01108     si.fMask = SIF_TRACKPOS|SIF_PAGE|SIF_RANGE;
01109     GetVScrollBar()->GetScrollInfo(&si);
01110 
01111     INT32 nStep = GetKeyState(CAMKEY(CONTROL))<0 ? si.nPage : ROWHEIGHT ;
01112 
01113     INT32 newPos = GetVScrollBar()->GetScrollPos() - nStep*zDelta/WHEEL_DELTA;
01114 
01115     // force to zero if the first line would appear partially off screen
01116     if(newPos < ROWHEIGHT)
01117         newPos = 0;
01118     // keep the upper limit within bounds
01119     if(newPos > si.nMax)
01120         newPos = si.nMax;
01121 
01122     if (m_ScrollableArea)
01123     {
01124         m_ScrollableArea->HandleScrollMessage(SB_THUMBPOSITION, newPos);
01125     }
01126     return CWnd::OnMouseWheel(nFlags, zDelta, pt);
01127 }
01128     

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