home *** CD-ROM | disk | FTP | other *** search
- // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
- //
- // WINDOWS SKINNING TUTORIAL - by Vander Nunes - virtware.net
- // This is the source-code that shows what is discussed in the tutorial.
- // The code is simplified for the sake of clarity, but all the needed
- // features for handling skinned windows is present. Please read
- // the article for more information.
- //
- // skin.cpp : CSkin class implementation
- // 28/02/2002 : initial release.
- //
- // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
-
- #include "stdafx.h"
- #include "skin.h"
- #include <stdio.h>
- #include "xImage.h"
-
- #include "../System.h"
-
- #ifdef _DEBUG
- #define new DEBUG_NEW
- #undef THIS_FILE
- static char THIS_FILE[] = __FILE__;
- #endif
-
- // ----------------------------------------------------------------------------
- // constructor 1 - use it when you have not already created the app window.
- // this one will not subclass automatically, you must call Hook() and Enable()
- // to subclass the app window and enable the skin respectively.
- // will throw an exception if unable to initialize skin from resource.
- // ----------------------------------------------------------------------------
-
- CSkin::CSkin()
- {
- // default starting values
- m_bHooked = false;
- m_OldWndProc = NULL;
-
- m_rect.top = 0;
- m_rect.bottom = 0;
- m_rect.right = 0;
- m_rect.left = 0;
-
- m_dOldStyle = 0;
-
- m_oldRect = m_rect;
- m_nButtons = 0;
- m_buttons = NULL;
- }
-
- // ----------------------------------------------------------------------------
- // destructor - just call the destroyer
- // ----------------------------------------------------------------------------
- CSkin::~CSkin()
- {
- Destroy();
- }
-
- HBITMAP CSkin::LoadImage(const char *filename)
- {
- CxImage image;
- image.Load(filename);
- if(!image.IsValid()) {
- return NULL;
- }
-
- return image.MakeBitmap(NULL);
- }
-
- // ----------------------------------------------------------------------------
- // Initialize the skin
- // ----------------------------------------------------------------------------
- bool CSkin::Initialize(const char *skinFile)
- {
- // try to retrieve the skin data from resource.
- bool res = GetSkinData(skinFile);
- if(!res)
- systemMessage(0, m_error);
- return res;
- }
-
- // ----------------------------------------------------------------------------
- // destroy skin resources and free allocated resources
- // ----------------------------------------------------------------------------
- void CSkin::Destroy()
- {
- if (m_buttons) {
- delete[] m_buttons;
- m_buttons = NULL;
- }
-
- // unhook the window
- UnHook();
-
- // free bitmaps and device context
- if (m_dcSkin) { SelectObject(m_dcSkin, m_hOldBmp); DeleteDC(m_dcSkin); m_dcSkin = NULL; }
- if (m_hBmp) { DeleteObject(m_hBmp); m_hBmp = NULL; }
-
- // free skin region
- if (m_rgnSkin) { DeleteObject(m_rgnSkin); m_rgnSkin = NULL; }
-
- }
-
-
-
- // ----------------------------------------------------------------------------
- // toggle skin on/off - must be Hooked() before attempting to enable skin.
- // ----------------------------------------------------------------------------
- bool CSkin::Enable(bool bEnable)
- {
- // refuse to enable if there is no window subclassed yet.
- if (!Hooked()) return false;
-
- // toggle
- m_bEnabled = bEnable;
-
- // force window repainting
- InvalidateRect(m_hWnd, NULL, TRUE);
-
- return true;
- }
-
-
-
- // ----------------------------------------------------------------------------
- // tell if the skinning is enabled
- // ----------------------------------------------------------------------------
- bool CSkin::Enabled()
- {
- return m_bEnabled;
- }
-
-
-
- // ----------------------------------------------------------------------------
- // hook a window
- // ----------------------------------------------------------------------------
- bool CSkin::Hook(CWnd *pWnd)
- {
- // unsubclass any other window
- if (Hooked()) UnHook();
-
- // this will be our new subclassed window
- m_hWnd = (HWND)*pWnd;
-
- // --------------------------------------------------
- // change window style (get rid of the caption bar)
- // --------------------------------------------------
- DWORD dwStyle = GetWindowLong(m_hWnd, GWL_STYLE);
- m_dOldStyle = dwStyle;
- dwStyle &= ~(WS_CAPTION|WS_SIZEBOX);
- SetWindowLong(m_hWnd, GWL_STYLE, dwStyle);
-
- RECT r;
- pWnd->GetWindowRect(&r);
- m_oldRect = r;
- pWnd->MoveWindow(r.left,
- r.top,
- m_iWidth,
- m_iHeight,
- FALSE);
-
- pWnd->SetMenu(NULL);
-
- if(m_rgnSkin != NULL)
- // set the skin region to the window
- pWnd->SetWindowRgn(m_rgnSkin, true);
-
- // subclass the window procedure
- m_OldWndProc = (WNDPROC)SetWindowLong(m_hWnd, GWL_WNDPROC, (LONG)SkinWndProc);
-
- // store a pointer to our class instance inside the window procedure.
- if (!SetProp(m_hWnd, "skin", (void*)this))
- {
- // if we fail to do so, we just can't activate the skin.
- UnHook();
- return false;
- }
-
-
- // update flag
- m_bHooked = ( m_OldWndProc ? true : false );
-
- for(int i = 0; i < m_nButtons; i++) {
- RECT r;
- m_buttons[i].GetRect(r);
- m_buttons[i].CreateButton("", WS_VISIBLE, r, pWnd, 0);
- }
-
- // force window repainting
- RedrawWindow(NULL,NULL,NULL,RDW_INVALIDATE|RDW_ERASE|RDW_ALLCHILDREN);
-
- // successful return if we're hooked.
- return m_bHooked;
- }
-
-
-
- // ----------------------------------------------------------------------------
- // unhook the window
- // ----------------------------------------------------------------------------
- bool CSkin::UnHook()
- {
- // just to be safe we'll check this
- WNDPROC OurWnd;
-
- // cannot unsubclass if there is no window subclassed
- // returns true anyways.
- if (!Hooked()) return true;
-
- if(m_rgnSkin != NULL)
- // remove the skin region from the window
- SetWindowRgn(m_hWnd, NULL, true);
-
- // unsubclass the window procedure
- OurWnd = (WNDPROC)SetWindowLong(m_hWnd, GWL_WNDPROC, (LONG)m_OldWndProc);
-
- // remove the pointer to our class instance, but if we fail we don't care.
- RemoveProp(m_hWnd, "skin");
-
- // update flag - if we can't get our window procedure address again,
- // we failed to unhook the window.
- m_bHooked = ( OurWnd ? false : true );
-
- SetWindowLong(m_hWnd, GWL_STYLE, m_dOldStyle);
-
- RECT r;
-
- GetWindowRect(m_hWnd, &r);
-
- MoveWindow(m_hWnd,
- r.left,
- r.top,
- m_oldRect.right - m_oldRect.left,
- m_oldRect.bottom - m_oldRect.top,
- FALSE);
-
- // force window repainting
- RedrawWindow(NULL,NULL,NULL,RDW_INVALIDATE|RDW_ERASE|RDW_ALLCHILDREN);
-
- // successful return if we're unhooked.
- return !m_bHooked;
- }
-
-
-
- // ----------------------------------------------------------------------------
- // tell us if there is a window subclassed
- // ----------------------------------------------------------------------------
- bool CSkin::Hooked()
- {
- return m_bHooked;
- }
-
-
-
- // ----------------------------------------------------------------------------
- // return the skin bitmap width
- // ----------------------------------------------------------------------------
- int CSkin::Width()
- {
- return m_iWidth;
- }
-
-
-
- // ----------------------------------------------------------------------------
- // return the skin bitmap height
- // ----------------------------------------------------------------------------
- int CSkin::Height()
- {
- return m_iHeight;
- }
-
-
-
- // ----------------------------------------------------------------------------
- // return the skin device context
- // ----------------------------------------------------------------------------
- HDC CSkin::HDC()
- {
- return m_dcSkin;
- }
-
- bool CSkin::ParseRect(char *buffer, RECT& rect)
- {
- char *token = strtok(buffer, ",");
-
- if(token == NULL)
- return false;
- rect.left = atoi(token);
-
- token = strtok(NULL, ",");
- if(token == NULL)
- return false;
- rect.top = atoi(token);
-
- token = strtok(NULL, ",");
- if(token == NULL)
- return false;
- rect.right = rect.left + atoi(token);
-
- token = strtok(NULL, ",");
- if(token == NULL)
- return false;
- rect.bottom = rect.top + atoi(token);
-
- token = strtok(NULL, ",");
- if(token != NULL)
- return false;
-
- return true;
- }
-
- HRGN CSkin::LoadRegion(const char *rgn)
- {
- // -------------------------------------------------
- // then, we retrieve the skin region from resource.
- // -------------------------------------------------
- FILE *f = fopen(rgn, "rb");
- if(!f) return NULL;
-
- fseek(f, 0, SEEK_END);
- int size = ftell(f);
- LPRGNDATA pSkinData = (LPRGNDATA)malloc(size);
- if(!pSkinData) {
- fclose(f);
- return NULL;
- }
-
- fseek(f, 0, SEEK_SET);
-
- fread(pSkinData, 1, size, f);
-
- fclose(f);
-
- // create the region using the binary data.
- HRGN r = ExtCreateRegion(NULL, size, pSkinData);
-
- // free the allocated resource
- free(pSkinData);
-
- return r;
- }
-
- // ----------------------------------------------------------------------------
- // skin retrieval helper
- // ----------------------------------------------------------------------------
- bool CSkin::GetSkinData(const char *skinFile)
- {
- // -------------------------------------------------
- // retrieve the skin bitmap from resource.
- // -------------------------------------------------
-
- char buffer[2048];
-
- if(!GetPrivateProfileString("skin", "image", "", buffer, 2048, skinFile)) {
- m_error = "Missing skin bitmap";
- return false;
- }
- CString bmpName = buffer;
- CString rgn = "";
- if(GetPrivateProfileString("skin", "region", "", buffer, 2048, skinFile)) {
- rgn = buffer;
- }
-
- if(!GetPrivateProfileString("skin", "draw", "", buffer, 2048, skinFile)) {
- m_error = "Missing draw rectangle";
- return false;
- }
-
- if(!ParseRect(buffer, m_rect)) {
- m_error = "Invalid draw rectangle";
- return false;
- }
-
- m_nButtons = GetPrivateProfileInt("skin", "buttons", 0, skinFile);
-
- if(m_nButtons) {
- m_buttons = new SkinButton[m_nButtons];
- for(int i = 0; i < m_nButtons; i++) {
- if(!ReadButton(skinFile, i))
- return false;
- }
- }
-
- CString path = skinFile;
- int index = path.ReverseFind('\\');
- if(index != -1) {
- path = path.Left(index+1);
- }
-
- bmpName = path + bmpName;
- if(strcmp(rgn, ""))
- rgn = path + rgn;
-
- m_hBmp = LoadImage(bmpName);
-
- if (!m_hBmp) {
- m_error = "Error loading skin bitmap " + bmpName;
- return false;
- }
-
- // get skin info
- BITMAP bmp;
- GetObject(m_hBmp, sizeof(bmp), &bmp);
-
- // get skin dimensions
- m_iWidth = bmp.bmWidth;
- m_iHeight = bmp.bmHeight;
-
- if(strcmp(rgn, "")) {
- m_rgnSkin = LoadRegion(rgn);
- if(m_rgnSkin == NULL) {
- m_error = "Error loading skin region " + rgn;
- return false;
- }
- }
-
- // -------------------------------------------------
- // well, things are looking good...
- // as a quick providence, just create and keep
- // a device context for our later blittings.
- // -------------------------------------------------
-
- // create a context compatible with the user desktop
- m_dcSkin = CreateCompatibleDC(0);
- if (!m_dcSkin) return false;
-
- // select our bitmap
- m_hOldBmp = (HBITMAP)SelectObject(m_dcSkin, m_hBmp);
-
-
- // -------------------------------------------------
- // done
- // -------------------------------------------------
- return true;
- }
-
- bool CSkin::ReadButton(const char *skinFile, int num)
- {
- char buffer[2048];
-
- CString path = skinFile;
- int index = path.ReverseFind('\\');
- if(index != -1) {
- path = path.Left(index+1);
- }
- sprintf(buffer, "button-%d", num);
- CString name = buffer;
-
- if(!GetPrivateProfileString(name, "normal", "", buffer, 2048, skinFile)) {
- m_error = "Missing button bitmap for " + name;
- return false;
- }
-
- CString normalBmp = path + buffer;
-
- HBITMAP bmp = LoadImage(normalBmp);
- if(!bmp) {
- m_error = "Error loading button bitmap " + normalBmp;
- return false;
- }
- m_buttons[num].SetNormalBitmap(bmp);
-
- if(!GetPrivateProfileString(name, "down", "", buffer, 2048, skinFile)) {
- m_error = "Missing button down bitmap " + name;
- return false;
- }
-
- CString downBmp = path + buffer;
-
- bmp = LoadImage(downBmp);
-
- if (!bmp) {
- m_error = "Error loading button down bitmap " + downBmp;
- return false;
- }
- m_buttons[num].SetDownBitmap(bmp);
-
- if(GetPrivateProfileString(name, "over", "", buffer, 2048, skinFile)) {
- CString overBmp = path + buffer;
-
- bmp = LoadImage(overBmp);
-
- if (!bmp) {
- m_error = "Error loading button over bitmap " + overBmp;
- return false;
- }
- m_buttons[num].SetOverBitmap(bmp);
- }
-
- if(GetPrivateProfileString(name, "region", "", buffer, 2048, skinFile)) {
- CString region = path + buffer;
-
- HRGN rgn = LoadRegion(region);
- if(!rgn) {
- m_error = "Error loading button region " + region;
- return false;
- }
- m_buttons[num].SetRegion(rgn);
- }
-
- if(!GetPrivateProfileString(name, "id", "", buffer, 2048, skinFile)) {
- "Missing button ID for " + name;
- return false;
- }
- m_buttons[num].SetId(buffer);
-
- if(!GetPrivateProfileString(name, "rect", "", buffer, 2048, skinFile)) {
- m_error = "Missing button rectangle for " + name;
- return false;
- }
-
- RECT r;
- if(!ParseRect(buffer, r)) {
- m_error = "Invalid button rectangle for " + name;
- return false;
- }
- m_buttons[num].SetRect(r);
-
- return true;
- }
-
- // ------------------------------------------------------------------------
- // Default skin window procedure.
- // Here the class will handle WM_PAINT and WM_LBUTTONDOWN, originally sent
- // to the application window, but now subclassed. Any other messages will
- // just pass through the procedure and reach the original app procedure.
- // ------------------------------------------------------------------------
- LRESULT CALLBACK SkinWndProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
- {
- // we will need a pointer to the associated class instance
- // (it was stored in the window before, remember?)
- CSkin *pSkin = (CSkin*)GetProp(hWnd, "skin");
-
- // to handle WM_PAINT
- PAINTSTRUCT ps;
-
- // if we fail to get our class instance, we can't handle anything.
- if (!pSkin) return DefWindowProc(hWnd,uMessage,wParam,lParam);
-
- switch(uMessage)
- {
- case WM_WINDOWPOSCHANGING:
- {
- LPWINDOWPOS pos = (LPWINDOWPOS)lParam;
- pos->cx = pSkin->Width();
- pos->cy = pSkin->Height();
- return 0L;
- }
- break;
-
- case WM_PAINT:
- {
- // ---------------------------------------------------------
- // here we just need to blit our skin
- // directly to the device context
- // passed by the painting message.
- // ---------------------------------------------------------
- BeginPaint(hWnd,&ps);
-
- // blit the skin
- BitBlt(ps.hdc,0,0,pSkin->Width(),pSkin->Height(),pSkin->HDC(),0,0,SRCCOPY);
-
- EndPaint(hWnd,&ps);
- break;
- }
-
- case WM_LBUTTONDOWN:
- {
- // ---------------------------------------------------------
- // this is a common trick for easy dragging of the window.
- // this message fools windows telling that the user is
- // actually dragging the application caption bar.
- // ---------------------------------------------------------
- SendMessage(hWnd, WM_NCLBUTTONDOWN, HTCAPTION,NULL);
- break;
- }
-
- }
-
- // ---------------------------------------------------------
- // call the default window procedure to keep things going.
- // ---------------------------------------------------------
- return CallWindowProc(pSkin->m_OldWndProc, hWnd, uMessage, wParam, lParam);
- }
-