root/trunk/GUIComponents/Splasher.cpp

Revision 5, 18.1 kB (checked in by qbert, 3 years ago)

Initial ( and last :( ) commit

Line 
1 /*
2 Module : SPLASHER.CPP
3 Purpose: A splash screen component for MFC which uses a DIB bitmap
4          instead of a resource. Palette management code is also included
5          so that the bitmap will display using its own optimized palette.
6          It also used as a UI thread so that UI of the splash screen
7          remains responsive.
8 Created: PJN / 15-11-1996
9 History: PJN / 11-12-1997 1) Incoporation of new DIB code provided by the authors own
10                           CDibImage class. A static library version is used by
11                           splasher here. If you want to use CDIbImage for anything
12                           other than CSplashThread, then you need to download
13                           CDibImage on its own.
14                           2) Can now load from resource or bitmap
15                           3) General tidy up of the code.
16          PJN / 22-3-1998  1) Palette is now correctly initialised on startup
17                           2) Code now protects itself if m_SplashScreen cannot be created
18          PJN / 22-12-1998 1) Now no longer dependent on CDibImage.
19                           2) Unicode Enabled the code,
20                           3) General tidy up of the code
21                           4) Removed the unnecessary variable m_bCreated
22                           5) Fixed a potential race condition in CSplashThread::HideSplash()
23          PJN / 01-03-2000 1) Fixed a problem with bitmaps which do not have a palette
24                           2) Fixed a problem in Win 98 and Win2000 when the splash screen is
25                           closed but the main window of your app fails to activate. The code
26                           now uses AttachThreadInput to synchronise the UI activities of the
27                           main GUI thread and the splash screen thread.
28          PJN / 01-01-2001 1) Now includes copyright message in the source code and documentation.
29                           2) Modified the way the sample app waits for the thread to exit. It
30                           now synchronises with the exit of the splash screen thread
31                           3) Now allows the icon of the splash screen to be specified
32                           4) Now allows the splash screen window to be customized at run time
33                           through a virtual function "CSplashThread::CreateSplash". Thanks to
34                           Yury Goltsman for suggesting these enhancements.
35          PJN / 27-08-2001 1) Splash screen now by default includes the window style WS_EX_TOPMOST
36          PJN / 31-08-2001 1) Fixed an assert which was occuring when you brought up the splash window
37                           and closed it really quickly. Thanks to Wanner Robert for spotting this problem.
38                           2) Removed a busy loop in HideSplash and used a Win32 event for synchronisation
39                           instead.
40                           3) Implemented a class factory design pattern to further simplify the use
41                           of the splash screen class. All client code now needs to do is derive a class
42                           from CSplashWnd (making sure it's declared DYNCREATE) and at a minimum just
43                           call some of it's methods in the constructor to determine what it will display
44                           at runtime. Then just use CSplashFactory::Create with the name of your derived
45                           class. When you want to close the splash screen just call
46                           CSplashFactory::Create. What could be simpler!!!.
47          PJN / 03-10-2002 1) Now allows you to specify a transparent color to draw the splash screen with.
48                           This allows you to realize shapes other than the standard rectangle. Thanks to
49                           Gero Gerber for this update. Please note that this support is only available on
50                           Windows 2000 or later.
51                           2) Made the destructor of CSplashWnd virtual
52          PJN / 11-10-2002 1) Fixed up some coding issues reported by BoundsChecker in CSplashWnd::OnPaint.
53                           2) Now supports a drop shadow effect on the splash screen (assuming that the
54                           client OS is Windows XP or later).
55          PJN / 28-08-2003 1) Removed a number of level 4 warnings.
56          PJN / 06-09-2003 1) Fixed a problem where you get a crash in the close method if the window to gain
57                           focus was NULL. Thanks to Darius Thabit for reporting this problem.
58          PJN / 09-05-2004 1) Updated the copyright details
59                           2) Included a new sample app in the download to demonstrate the correct usage of
60                           the splash screen in a dialog based app.
61                           3) Fixed a bug where sometimes the splash screen component would get stuck while
62                           waiting for the splash screen thread to exit. The fix is to request the parent
63                           window to exit in CSplashWnd::OnClose. Thanks to Frederic Metrich for reporting
64                           this issue.
65                           
66
67 Copyright (c) 1996 - 2004 by PJ Naughter.  (Web: www.naughter.com, Email: pjna@naughter.com)
68
69 All rights reserved.
70
71 Copyright / Usage Details:
72
73 You are allowed to include the source code in any product (commercial, shareware, freeware or otherwise)
74 when your product is released in binary form. You are allowed to modify the source code in any way you want
75 except you cannot modify the copyright details at the top of each module. If you want to distribute source
76 code with your application, then you are only allowed to distribute versions released by the author. This is
77 to maintain a single distribution point for the source code.
78
79 */
80
81
82 //////////////////// Includes //////////////////////////
83
84 #include "stdafx.h"
85 #include "Splasher.h"
86
87
88
89 //////////////////// Defines ///////////////////////////
90
91 #ifdef _DEBUG
92 #define new DEBUG_NEW
93 #undef THIS_FILE
94 static char BASED_CODE THIS_FILE[] = __FILE__;
95 #endif
96
97 //Define the various constants we need to call SetLayeredWindowAttributes.
98 //Doing this means that the code does not required the latest Platform
99 //SDK to be installed
100 #ifndef WS_EX_LAYERED
101 #define WS_EX_LAYERED           0x00080000
102 #endif
103 #ifndef LWA_COLORKEY
104 #define LWA_COLORKEY            0x00000001
105 #endif
106
107 //Define for drop shadow support. Doing this means that the code does
108 //not require the latest Platform SDK to be installed
109 #ifndef CS_DROPSHADOW
110 #define CS_DROPSHADOW       0x00020000
111 #endif
112
113
114 //////////////////// Implementation ////////////////////
115
116 BEGIN_MESSAGE_MAP(CSplashWnd, CWnd)
117   //{{AFX_MSG_MAP(CSplashWnd)
118   ON_WM_CREATE()
119   ON_WM_PAINT()
120   ON_WM_LBUTTONDOWN()
121   ON_WM_CLOSE()
122     //}}AFX_MSG_MAP
123 END_MESSAGE_MAP()
124
125 IMPLEMENT_DYNCREATE(CSplashWnd, CWnd);
126
127 CSplashWnd::CSplashWnd()
128 {
129   m_bOKToClose = FALSE;
130   m_nHeight = 0;
131   m_nWidth = 0;
132   m_bUseFile = FALSE;
133   m_pszResourceName = NULL;
134   m_hIcon = NULL;
135   m_bTransparent = FALSE;
136   m_clrTransparent = RGB(255, 0, 255);
137   m_bDraggable = TRUE;
138   m_bDropShadow = FALSE;
139
140   //Determine if the OS supports layered windows
141   HINSTANCE hUser32 = GetModuleHandle(_T("USER32.DLL"));
142   if (hUser32)
143     m_lpfnSetLayeredWindowAttributes = (lpfnSetLayeredWindowAttributes) GetProcAddress(hUser32, "SetLayeredWindowAttributes");
144   else
145     m_lpfnSetLayeredWindowAttributes = NULL;
146 }
147
148 CSplashWnd::~CSplashWnd()
149 {
150 }
151
152 BOOL CSplashWnd::LoadBitmap()
153 {    
154   //Use LoadImage to get the image loaded into a DIBSection
155   HBITMAP hBitmap;
156   if (m_bUseFile)
157     hBitmap = (HBITMAP) ::LoadImage(NULL, m_sFilename, IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION | LR_DEFAULTSIZE | LR_LOADFROMFILE);
158   else
159     hBitmap = (HBITMAP) ::LoadImage(AfxGetResourceHandle(), m_pszResourceName, IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION | LR_DEFAULTSIZE);
160
161   //Check that we could load it up 
162   if (hBitmap == NULL)       
163   {
164     ASSERT(FALSE); //Did you forget to derive your class from CSplashWnd and setup its member variables correctly\n"));
165     return FALSE;
166   }
167
168   //Get the width and height of the DIBSection
169   BITMAP bm;
170   GetObject(hBitmap, sizeof(BITMAP), &bm);
171   m_nHeight = bm.bmHeight;
172   m_nWidth = bm.bmWidth;
173
174   //Covert from the SDK bitmap handle to the MFC equivalent
175   m_Bitmap.Attach(hBitmap);
176
177   return TRUE;   
178 }
179
180 BOOL CSplashWnd::SetTransparent(COLORREF clrTransparent)
181 {
182   BOOL bSuccess = FALSE;
183
184   if (m_lpfnSetLayeredWindowAttributes)
185   {
186     m_bTransparent = TRUE;
187     m_clrTransparent = clrTransparent;
188     bSuccess = TRUE;
189   }
190
191   return bSuccess;
192 }
193
194 void CSplashWnd::SetDraggable(BOOL bDraggable)
195 {
196   m_bDraggable = bDraggable;
197 }
198
199 void CSplashWnd::SetDropShadow(BOOL bDropShadow)
200 {
201   m_bDropShadow = bDropShadow;
202 }
203
204 void CSplashWnd::CreatePaletteFromBitmap()
205 {
206   //Get the color depth of the DIBSection
207   BITMAP bm;
208   m_Bitmap.GetObject(sizeof(BITMAP), &bm);
209
210   //If the DIBSection is 256 color or less, it has a color table
211   if ((bm.bmBitsPixel * bm.bmPlanes) <= 8)     
212   {
213     //Create a memory DC and select the DIBSection into it
214     CDC memDC;
215     memDC.CreateCompatibleDC(NULL);
216     CBitmap* pOldBitmap = memDC.SelectObject(&m_Bitmap);
217
218     //Get the DIBSection's color table
219     RGBQUAD rgb[256];
220     ::GetDIBColorTable(memDC.m_hDC, 0, 256, rgb);
221
222     //Create a palette from the color table
223     LPLOGPALETTE pLogPal = (LPLOGPALETTE) new BYTE[sizeof(LOGPALETTE) + (256*sizeof(PALETTEENTRY))];
224     pLogPal->palVersion = 0x300;       
225     pLogPal->palNumEntries = 256;
226
227     for (WORD i=0; i<256; i++)       
228     {
229       pLogPal->palPalEntry[i].peRed = rgb[i].rgbRed;
230       pLogPal->palPalEntry[i].peGreen = rgb[i].rgbGreen;
231       pLogPal->palPalEntry[i].peBlue = rgb[i].rgbBlue;
232       pLogPal->palPalEntry[i].peFlags = 0;
233     }
234     VERIFY(m_Palette.CreatePalette(pLogPal));
235    
236     //Clean up
237     delete pLogPal;
238     memDC.SelectObject(pOldBitmap);
239   }
240   else  //It has no color table, so use a halftone palette     
241   {
242     CDC* pRefDC = GetDC();
243     m_Palette.CreateHalftonePalette(pRefDC);
244     ReleaseDC(pRefDC);     
245   }     
246 }
247
248 BOOL CSplashWnd::Create()
249 {                  
250   //Load up the bitmap from file or from resource
251   VERIFY(LoadBitmap());
252
253   //Modify the owner window of the splash screen to be an invisible WS_POPUP
254   //window so that the splash screen does not appear in the task bar
255   LPCTSTR pszWndClassOwner = AfxRegisterWndClass(0, 0, 0, m_hIcon);
256   VERIFY(m_wndOwner.CreateEx(0, pszWndClassOwner, _T(""), WS_POPUP, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, 0));
257
258   //Create the appropiate window class to use for the splash window
259   LPCTSTR pszWndClass = NULL;
260   if (m_bDropShadow)
261   {
262     try
263     {
264       pszWndClass = AfxRegisterWndClass(CS_DROPSHADOW, AfxGetApp()->LoadStandardCursor(IDC_ARROW), 0, m_hIcon);
265     }
266     catch(CResourceException* pEx)
267     {
268       //Avoid the memory leak
269       pEx->Delete();
270
271       //Probably because CS_DROPSHADOW is not supported on this OS, try again without it
272       pszWndClass = AfxRegisterWndClass(0, AfxGetApp()->LoadStandardCursor(IDC_ARROW), 0, m_hIcon);
273     }
274   }
275   else
276     pszWndClass = AfxRegisterWndClass(0, AfxGetApp()->LoadStandardCursor(IDC_ARROW), 0, m_hIcon);
277
278   //Create this window
279   VERIFY(CreateEx(WS_EX_TOPMOST, pszWndClass, _T(""), WS_POPUP | WS_VISIBLE, 0, 0, m_nWidth, m_nHeight, m_wndOwner.GetSafeHwnd(), NULL));
280
281   //Make the window transparent if required to
282   if (m_bTransparent && m_lpfnSetLayeredWindowAttributes)
283   {
284     ASSERT(m_lpfnSetLayeredWindowAttributes);
285
286     //Add the transparent style
287     LONG nStyle = GetWindowLong(m_hWnd, GWL_EXSTYLE);
288     SetWindowLong(m_hWnd, GWL_EXSTYLE, nStyle | WS_EX_LAYERED);
289
290     //Make the Window transparent used the specifed color
291     m_lpfnSetLayeredWindowAttributes(m_hWnd, m_clrTransparent, 0, LWA_COLORKEY);
292   }
293
294   //Create the palette, We need to do this after the window is created because
295   //we may need to access the DC associated with it
296   CreatePaletteFromBitmap();
297
298   //realize the bitmap's palette into the DC
299   OnQueryNewPalette();
300
301   return TRUE;
302 }
303
304 int CSplashWnd::OnCreate(LPCREATESTRUCT lpCreateStruct)
305 {
306   //Let the parent class do its thing
307   if (CWnd::OnCreate(lpCreateStruct) == -1)
308     return -1;
309
310   //Center the splash window on the screen
311   CenterWindow();
312
313   return 0;
314 }
315
316 void CSplashWnd::OnPaint()
317 {
318   CPaintDC dc(this);
319  
320   //select the palette and bitmap into the DC
321   CDC memDC;
322   memDC.CreateCompatibleDC(&dc);
323   CBitmap* pOldBitmap = memDC.SelectObject(&m_Bitmap);
324   CPalette* pOldPalette = NULL;
325   if (m_Palette.m_hObject)
326     pOldPalette = dc.SelectPalette(&m_Palette, FALSE);
327   dc.RealizePalette();
328   dc.BitBlt(0, 0, m_nWidth, m_nHeight, &memDC, 0, 0, SRCCOPY);
329   memDC.SelectObject(pOldBitmap);         
330   if (pOldPalette)
331     dc.SelectPalette(pOldPalette, FALSE);
332 }
333
334 void CSplashWnd::OnLButtonDown(UINT /*nFlags*/, CPoint /*point*/)
335 {
336   if (m_bDraggable)
337   {
338     //Fake a Window drag
339     SendMessage(WM_LBUTTONUP);
340     SendMessage(WM_SYSCOMMAND, 0xF012);
341   }
342 }
343
344 void CSplashWnd::OnClose()
345 {
346   if (m_bOKToClose)
347   {
348     //Just request the parent window to close which will cause this window to be destroyed
349     m_wndOwner.PostMessage(WM_CLOSE);
350   }
351 }
352
353 BOOL CSplashWnd::SelRelPal(BOOL bForceBkgnd)
354 {
355   // We are going active, so realize our palette.
356   CDC* pDC = GetDC();
357
358   CPalette* pOldPal = pDC->SelectPalette(&m_Palette, bForceBkgnd);
359   UINT u = pDC->RealizePalette();
360   pDC->SelectPalette(pOldPal, TRUE);
361   pDC->RealizePalette();
362
363   ReleaseDC(pDC);
364
365   // If any colors have changed or we are in the
366   // background, repaint the lot.
367   if (u || bForceBkgnd)
368     InvalidateRect(NULL, TRUE); // Repaint.
369  
370   return (BOOL) u; // TRUE if some colors changed.
371 }
372
373 void CSplashWnd::OnPaletteChanged(CWnd* pFocusWnd)
374 {
375   // See if the change was caused by us and ignore it if not.
376   if (pFocusWnd != this)
377     SelRelPal(TRUE); // Realize in the background.
378 }
379
380 BOOL CSplashWnd::OnQueryNewPalette()
381 {
382   return SelRelPal(FALSE); // Realize in the foreground.
383 }
384
385 void CSplashWnd::SetBitmapToUse(const CString& sFilename)
386 {
387   m_bUseFile = TRUE;
388   m_sFilename = sFilename;
389 }
390
391 void CSplashWnd::SetBitmapToUse(UINT nResourceID)
392 {
393   m_bUseFile = FALSE;
394   m_pszResourceName = MAKEINTRESOURCE(nResourceID);
395 }
396
397 void CSplashWnd::SetBitmapToUse(LPCTSTR pszResourceName)
398 {
399   m_bUseFile = FALSE;
400   m_pszResourceName = pszResourceName;
401 }
402
403 void CSplashWnd::SetIcon(HICON hIcon)
404 {
405   //Destroy the icon if we have one already loaded
406   if (m_hIcon)
407     DestroyIcon(m_hIcon);
408      
409   m_hIcon = hIcon;
410 }
411
412
413
414 BEGIN_MESSAGE_MAP(CSplashThread, CWinThread)
415     //{{AFX_MSG_MAP(CSplashThread)
416     //}}AFX_MSG_MAP
417 END_MESSAGE_MAP()
418
419 IMPLEMENT_DYNCREATE(CSplashThread, CWinThread)
420
421 CSplashThread::CSplashThread()
422 {
423   m_pRuntimeClassSplashWnd = NULL;
424   m_pSplashScreen = NULL;
425   m_bInitOK = FALSE;
426 }
427
428 CSplashThread::~CSplashThread()
429 {
430   delete m_pSplashScreen;
431 }
432
433 BOOL CSplashThread::InitInstance()
434 {
435   //Validate our parameters
436   ASSERT(m_pRuntimeClassSplashWnd);
437
438   //Create the Splash Screen window object
439   m_pSplashScreen = (CSplashWnd*) m_pRuntimeClassSplashWnd->CreateObject();
440   if (m_pSplashScreen == NULL)
441   {
442     TRACE(_T("Failed to instantiate CSplashWnd object\n"));
443     m_SplashCreated.SetEvent();
444     return FALSE;
445   }
446  
447   //Validate that it is derived from SSplashWnd
448   ASSERT(m_pSplashScreen->IsKindOf(RUNTIME_CLASS(CSplashWnd)));
449
450   //Create the HWND of it
451   if (!m_pSplashScreen->Create())
452   {
453     TRACE(_T("Failed to creat CSplashWnd object\n"));
454
455     //Tidy up the heap memory we have allocated
456     delete m_pSplashScreen;
457
458     m_SplashCreated.SetEvent();
459     return FALSE;
460   }
461   m_pMainWnd = m_pSplashScreen;
462
463   //Signal the event which indicates that the splash window has been created
464   m_bInitOK = TRUE;
465   m_SplashCreated.SetEvent();
466
467     return TRUE;
468 }
469
470
471
472 CSplashFactory::CSplashFactory()
473 {
474   m_pSplashThread = NULL;
475 }
476
477 CSplashFactory::~CSplashFactory()
478 {
479   //Just incase client code forgot to call Close, we do it anyway
480   Close();
481 }
482
483 BOOL CSplashFactory::Create(CRuntimeClass* pRuntimeClassSplashWnd)
484 {
485   //Validate our parameters
486   ASSERT(m_pSplashThread == NULL);
487   ASSERT(pRuntimeClassSplashWnd);
488
489   //Create the GUI worker thread
490   m_pSplashThread = (CSplashThread*) AfxBeginThread(RUNTIME_CLASS(CSplashThread), THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED);
491   if (m_pSplashThread == NULL)
492   {
493     TRACE(_T("Failed to create splash screen thread\n"));
494     return FALSE;
495   }
496
497   ASSERT(m_pSplashThread->IsKindOf(RUNTIME_CLASS(CSplashThread)));
498   m_pSplashThread->m_pRuntimeClassSplashWnd = pRuntimeClassSplashWnd;
499   m_pSplashThread->m_bAutoDelete = FALSE; //We are in charge of the deletion of the thread
500   m_pSplashThread->ResumeThread();        //Resume the thread now that we have set it up
501
502   return TRUE;
503 }
504
505 BOOL CSplashFactory::Close(CWnd* pWndToGainFocus)
506 {
507   //Assume the worst
508   BOOL bSuccess = FALSE;
509
510   if (m_pSplashThread)
511   {
512     //Wait until the InitInstance has completed in worker thread
513     WaitForSingleObject(m_pSplashThread->m_SplashCreated, INFINITE);
514
515     //Attach this threads UI state to the worker threadss one, This will ensure that
516     //the activation state is managed consistenly across the two threads
517     ASSERT(AfxGetApp());
518     AttachThreadInput(AfxGetApp()->m_nThreadID, m_pSplashThread->m_nThreadID, TRUE);
519
520     //Set the focus back to the correct window
521     CWnd* pWndFocus = pWndToGainFocus;
522     if (pWndFocus == NULL)
523       pWndFocus = AfxGetMainWnd();
524
525     if (pWndFocus)
526     {
527       pWndFocus->SetFocus();
528       pWndFocus->SetForegroundWindow();
529     }
530
531     //If the worker thread has a splash screen then ask it to close
532     //before trying to close it
533     if (m_pSplashThread->m_pSplashScreen)
534     {
535       m_pSplashThread->m_pSplashScreen->SetOKToClose();
536       m_pSplashThread->m_pSplashScreen->PostMessage(WM_CLOSE);
537     }
538
539     //Wait for it to exit and tidy up its memory
540     WaitForSingleObject(m_pSplashThread->m_hThread, INFINITE);
541     delete m_pSplashThread;
542     m_pSplashThread = NULL;
543
544     bSuccess = TRUE;
545   }
546
547   return bSuccess;
548 }
Note: See TracBrowser for help on using the browser.