home *** CD-ROM | disk | FTP | other *** search
/ Chip 2004 July / CMCD0704.ISO / Software / Freeware / Utilitare / VisualBoyAdvance-1.7.2 / src / win32 / skin.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2004-02-29  |  15.2 KB  |  589 lines

  1. // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  2. //
  3. // WINDOWS SKINNING TUTORIAL - by Vander Nunes - virtware.net
  4. // This is the source-code that shows what is discussed in the tutorial.
  5. // The code is simplified for the sake of clarity, but all the needed
  6. // features for handling skinned windows is present. Please read
  7. // the article for more information.
  8. //
  9. // skin.cpp   : CSkin class implementation
  10. // 28/02/2002 : initial release.
  11. //
  12. // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  13.  
  14. #include "stdafx.h"
  15. #include "skin.h"
  16. #include <stdio.h>
  17. #include "xImage.h"
  18.  
  19. #include "../System.h"
  20.  
  21. #ifdef _DEBUG
  22. #define new DEBUG_NEW
  23. #undef THIS_FILE
  24. static char THIS_FILE[] = __FILE__;
  25. #endif
  26.  
  27. // ----------------------------------------------------------------------------
  28. // constructor 1 - use it when you have not already created the app window.
  29. // this one will not subclass automatically, you must call Hook() and Enable()
  30. // to subclass the app window and enable the skin respectively.
  31. // will throw an exception if unable to initialize skin from resource.
  32. // ----------------------------------------------------------------------------
  33.  
  34. CSkin::CSkin()
  35. {
  36.   // default starting values
  37.   m_bHooked = false;
  38.   m_OldWndProc = NULL;
  39.  
  40.   m_rect.top = 0;
  41.   m_rect.bottom = 0;
  42.   m_rect.right = 0;
  43.   m_rect.left = 0;
  44.  
  45.   m_dOldStyle = 0;
  46.  
  47.   m_oldRect = m_rect;
  48.   m_nButtons = 0;
  49.   m_buttons = NULL;
  50. }
  51.  
  52. // ----------------------------------------------------------------------------
  53. // destructor - just call the destroyer
  54. // ----------------------------------------------------------------------------
  55. CSkin::~CSkin()
  56. {
  57.   Destroy();
  58. }
  59.  
  60. HBITMAP CSkin::LoadImage(const char *filename)
  61. {
  62.   CxImage image;
  63.   image.Load(filename);
  64.   if(!image.IsValid()) {
  65.     return NULL;
  66.   }
  67.   
  68.   return image.MakeBitmap(NULL);
  69. }
  70.  
  71. // ----------------------------------------------------------------------------
  72. // Initialize the skin
  73. // ----------------------------------------------------------------------------
  74. bool CSkin::Initialize(const char *skinFile)
  75. {
  76.   // try to retrieve the skin data from resource.
  77.   bool res = GetSkinData(skinFile);
  78.   if(!res) 
  79.     systemMessage(0, m_error);
  80.   return res;
  81. }  
  82.  
  83. // ----------------------------------------------------------------------------
  84. // destroy skin resources and free allocated resources
  85. // ----------------------------------------------------------------------------
  86. void CSkin::Destroy()
  87. {
  88.   if (m_buttons) {
  89.     delete[] m_buttons;
  90.     m_buttons = NULL;
  91.   }
  92.  
  93.   // unhook the window
  94.   UnHook();
  95.  
  96.   // free bitmaps and device context
  97.   if (m_dcSkin) { SelectObject(m_dcSkin, m_hOldBmp); DeleteDC(m_dcSkin); m_dcSkin = NULL; }
  98.   if (m_hBmp) { DeleteObject(m_hBmp); m_hBmp = NULL; }
  99.  
  100.   // free skin region
  101.   if (m_rgnSkin) { DeleteObject(m_rgnSkin); m_rgnSkin = NULL; }
  102.   
  103. }
  104.  
  105.  
  106.  
  107. // ----------------------------------------------------------------------------
  108. // toggle skin on/off - must be Hooked() before attempting to enable skin.
  109. // ----------------------------------------------------------------------------
  110. bool CSkin::Enable(bool bEnable)
  111. {
  112.   // refuse to enable if there is no window subclassed yet.
  113.   if (!Hooked()) return false;
  114.  
  115.   // toggle
  116.   m_bEnabled = bEnable;
  117.  
  118.   // force window repainting
  119.   InvalidateRect(m_hWnd, NULL, TRUE);
  120.  
  121.   return true;
  122. }
  123.  
  124.  
  125.  
  126. // ----------------------------------------------------------------------------
  127. // tell if the skinning is enabled
  128. // ----------------------------------------------------------------------------
  129. bool CSkin::Enabled()
  130. {
  131.   return m_bEnabled;
  132. }
  133.  
  134.  
  135.  
  136. // ----------------------------------------------------------------------------
  137. // hook a window
  138. // ----------------------------------------------------------------------------
  139. bool CSkin::Hook(CWnd *pWnd)
  140. {
  141.   // unsubclass any other window
  142.   if (Hooked()) UnHook();
  143.  
  144.   // this will be our new subclassed window
  145.   m_hWnd = (HWND)*pWnd;
  146.  
  147.   // --------------------------------------------------
  148.   // change window style (get rid of the caption bar)
  149.   // --------------------------------------------------
  150.   DWORD dwStyle = GetWindowLong(m_hWnd, GWL_STYLE);
  151.   m_dOldStyle = dwStyle;
  152.   dwStyle &= ~(WS_CAPTION|WS_SIZEBOX);
  153.   SetWindowLong(m_hWnd, GWL_STYLE, dwStyle);
  154.  
  155.   RECT r;
  156.   pWnd->GetWindowRect(&r);
  157.   m_oldRect = r;
  158.   pWnd->MoveWindow(r.left,
  159.                    r.top,
  160.                    m_iWidth,
  161.                    m_iHeight,
  162.                    FALSE);
  163.   
  164.   pWnd->SetMenu(NULL);
  165.   
  166.   if(m_rgnSkin != NULL)
  167.     // set the skin region to the window
  168.     pWnd->SetWindowRgn(m_rgnSkin, true);    
  169.  
  170.   // subclass the window procedure
  171.   m_OldWndProc = (WNDPROC)SetWindowLong(m_hWnd, GWL_WNDPROC, (LONG)SkinWndProc);
  172.  
  173.   // store a pointer to our class instance inside the window procedure.
  174.   if (!SetProp(m_hWnd, "skin", (void*)this))
  175.     {
  176.       // if we fail to do so, we just can't activate the skin.
  177.       UnHook();
  178.       return false;
  179.     }
  180.  
  181.   
  182.   // update flag
  183.   m_bHooked = ( m_OldWndProc ? true : false );
  184.  
  185.   for(int i = 0; i < m_nButtons; i++) {
  186.     RECT r;
  187.     m_buttons[i].GetRect(r);
  188.     m_buttons[i].CreateButton("", WS_VISIBLE, r, pWnd, 0);
  189.   }
  190.   
  191.   // force window repainting
  192.   RedrawWindow(NULL,NULL,NULL,RDW_INVALIDATE|RDW_ERASE|RDW_ALLCHILDREN);  
  193.  
  194.   // successful return if we're hooked.
  195.   return m_bHooked;
  196. }
  197.  
  198.  
  199.  
  200. // ----------------------------------------------------------------------------
  201. // unhook the window
  202. // ----------------------------------------------------------------------------
  203. bool CSkin::UnHook()
  204. {
  205.   // just to be safe we'll check this
  206.   WNDPROC OurWnd;
  207.  
  208.   // cannot unsubclass if there is no window subclassed
  209.   // returns true anyways.
  210.   if (!Hooked()) return true;
  211.   
  212.   if(m_rgnSkin != NULL)
  213.     // remove the skin region from the window
  214.     SetWindowRgn(m_hWnd, NULL, true);
  215.  
  216.   // unsubclass the window procedure
  217.   OurWnd = (WNDPROC)SetWindowLong(m_hWnd, GWL_WNDPROC, (LONG)m_OldWndProc);
  218.  
  219.   // remove the pointer to our class instance, but if we fail we don't care.
  220.   RemoveProp(m_hWnd, "skin");
  221.  
  222.   // update flag - if we can't get our window procedure address again,
  223.   // we failed to unhook the window.
  224.   m_bHooked = ( OurWnd ? false : true );
  225.  
  226.   SetWindowLong(m_hWnd, GWL_STYLE, m_dOldStyle);
  227.  
  228.   RECT r;
  229.  
  230.   GetWindowRect(m_hWnd, &r);
  231.   
  232.   MoveWindow(m_hWnd,
  233.              r.left,
  234.              r.top,
  235.              m_oldRect.right - m_oldRect.left,
  236.              m_oldRect.bottom - m_oldRect.top,
  237.              FALSE);
  238.   
  239.   // force window repainting
  240.   RedrawWindow(NULL,NULL,NULL,RDW_INVALIDATE|RDW_ERASE|RDW_ALLCHILDREN);  
  241.  
  242.   // successful return if we're unhooked.
  243.   return !m_bHooked;
  244. }
  245.  
  246.  
  247.  
  248. // ----------------------------------------------------------------------------
  249. // tell us if there is a window subclassed
  250. // ----------------------------------------------------------------------------
  251. bool CSkin::Hooked()
  252. {
  253.   return m_bHooked;
  254. }
  255.  
  256.  
  257.  
  258. // ----------------------------------------------------------------------------
  259. // return the skin bitmap width
  260. // ----------------------------------------------------------------------------
  261. int CSkin::Width()
  262. {
  263.   return m_iWidth;
  264. }
  265.  
  266.  
  267.  
  268. // ----------------------------------------------------------------------------
  269. // return the skin bitmap height
  270. // ----------------------------------------------------------------------------
  271. int CSkin::Height()
  272. {
  273.   return m_iHeight;
  274. }
  275.  
  276.  
  277.  
  278. // ----------------------------------------------------------------------------
  279. // return the skin device context
  280. // ----------------------------------------------------------------------------
  281. HDC CSkin::HDC()
  282. {
  283.   return m_dcSkin;
  284. }
  285.  
  286. bool CSkin::ParseRect(char *buffer, RECT& rect)
  287. {
  288.   char *token = strtok(buffer, ",");
  289.  
  290.   if(token == NULL)
  291.     return false;
  292.   rect.left = atoi(token);
  293.  
  294.   token = strtok(NULL, ",");
  295.   if(token == NULL)
  296.     return false;
  297.   rect.top = atoi(token);
  298.  
  299.   token = strtok(NULL, ",");
  300.   if(token == NULL)
  301.     return false;
  302.   rect.right = rect.left + atoi(token);
  303.  
  304.   token = strtok(NULL, ",");
  305.   if(token == NULL)
  306.     return false;
  307.   rect.bottom = rect.top + atoi(token);
  308.  
  309.   token = strtok(NULL, ",");
  310.   if(token != NULL)
  311.     return false;
  312.  
  313.   return true;
  314. }
  315.  
  316. HRGN CSkin::LoadRegion(const char *rgn)
  317. {
  318.   // -------------------------------------------------
  319.   // then, we retrieve the skin region from resource.
  320.   // -------------------------------------------------
  321.   FILE *f = fopen(rgn, "rb");
  322.   if(!f) return NULL;
  323.   
  324.   fseek(f, 0, SEEK_END);
  325.   int size = ftell(f);
  326.   LPRGNDATA pSkinData = (LPRGNDATA)malloc(size);
  327.   if(!pSkinData) {
  328.     fclose(f);
  329.     return NULL;
  330.   }
  331.  
  332.   fseek(f, 0, SEEK_SET);
  333.   
  334.   fread(pSkinData, 1, size, f);
  335.   
  336.   fclose(f);
  337.   
  338.   // create the region using the binary data.
  339.   HRGN r = ExtCreateRegion(NULL, size, pSkinData);
  340.   
  341.   // free the allocated resource
  342.   free(pSkinData);
  343.   
  344.   return r;
  345. }
  346.  
  347. // ----------------------------------------------------------------------------
  348. // skin retrieval helper
  349. // ----------------------------------------------------------------------------
  350. bool CSkin::GetSkinData(const char *skinFile)
  351. {
  352.   // -------------------------------------------------
  353.   // retrieve the skin bitmap from resource.
  354.   // -------------------------------------------------
  355.  
  356.   char buffer[2048];
  357.  
  358.   if(!GetPrivateProfileString("skin", "image", "", buffer, 2048, skinFile)) {
  359.     m_error = "Missing skin bitmap";
  360.     return false;
  361.   }
  362.   CString bmpName = buffer;
  363.   CString rgn = "";
  364.   if(GetPrivateProfileString("skin", "region", "", buffer, 2048, skinFile)) {
  365.     rgn = buffer;
  366.   }
  367.  
  368.   if(!GetPrivateProfileString("skin", "draw", "", buffer, 2048, skinFile)) {
  369.     m_error = "Missing draw rectangle";
  370.     return false;
  371.   }
  372.   
  373.   if(!ParseRect(buffer, m_rect)) {
  374.     m_error = "Invalid draw rectangle";
  375.     return false;
  376.   }
  377.  
  378.   m_nButtons = GetPrivateProfileInt("skin", "buttons", 0, skinFile);
  379.  
  380.   if(m_nButtons) {
  381.     m_buttons = new SkinButton[m_nButtons];
  382.     for(int i = 0; i < m_nButtons; i++) {
  383.       if(!ReadButton(skinFile, i))
  384.         return false;
  385.     }
  386.   }
  387.   
  388.   CString path = skinFile;
  389.   int index = path.ReverseFind('\\');
  390.   if(index != -1) {
  391.     path = path.Left(index+1);
  392.   }
  393.  
  394.   bmpName = path + bmpName;
  395.   if(strcmp(rgn, ""))
  396.     rgn = path + rgn;
  397.  
  398.   m_hBmp = LoadImage(bmpName);
  399.  
  400.   if (!m_hBmp) {
  401.     m_error = "Error loading skin bitmap " + bmpName;
  402.     return false;
  403.   }
  404.  
  405.   // get skin info
  406.   BITMAP bmp;
  407.   GetObject(m_hBmp, sizeof(bmp), &bmp);
  408.  
  409.   // get skin dimensions
  410.   m_iWidth = bmp.bmWidth;
  411.   m_iHeight = bmp.bmHeight;
  412.  
  413.   if(strcmp(rgn, "")) {
  414.     m_rgnSkin = LoadRegion(rgn);
  415.     if(m_rgnSkin == NULL) {
  416.       m_error = "Error loading skin region " + rgn;
  417.       return false;
  418.     }
  419.   }
  420.  
  421.   // -------------------------------------------------
  422.   // well, things are looking good...
  423.   // as a quick providence, just create and keep
  424.   // a device context for our later blittings.
  425.   // -------------------------------------------------
  426.  
  427.   // create a context compatible with the user desktop
  428.   m_dcSkin = CreateCompatibleDC(0);
  429.   if (!m_dcSkin) return false;
  430.  
  431.   // select our bitmap
  432.   m_hOldBmp = (HBITMAP)SelectObject(m_dcSkin, m_hBmp);
  433.  
  434.  
  435.   // -------------------------------------------------
  436.   // done
  437.   // -------------------------------------------------
  438.   return true;
  439. }
  440.  
  441. bool CSkin::ReadButton(const char *skinFile, int num)
  442. {
  443.   char buffer[2048];
  444.  
  445.   CString path = skinFile;
  446.   int index = path.ReverseFind('\\');
  447.   if(index != -1) {
  448.     path = path.Left(index+1);
  449.   }
  450.   sprintf(buffer, "button-%d", num);
  451.   CString name = buffer;
  452.   
  453.   if(!GetPrivateProfileString(name, "normal", "", buffer, 2048, skinFile)) {
  454.     m_error = "Missing button bitmap for " + name;
  455.     return false;
  456.   }
  457.   
  458.   CString normalBmp = path + buffer;
  459.  
  460.   HBITMAP bmp = LoadImage(normalBmp);
  461.   if(!bmp) {
  462.     m_error = "Error loading button bitmap " + normalBmp;
  463.     return false;
  464.   }
  465.   m_buttons[num].SetNormalBitmap(bmp);
  466.  
  467.   if(!GetPrivateProfileString(name, "down", "", buffer, 2048, skinFile)) {
  468.     m_error = "Missing button down bitmap " + name;
  469.     return false;
  470.   }
  471.   
  472.   CString downBmp = path + buffer;
  473.  
  474.   bmp = LoadImage(downBmp);
  475.  
  476.   if (!bmp) {
  477.     m_error = "Error loading button down bitmap " + downBmp;
  478.     return false;
  479.   }
  480.   m_buttons[num].SetDownBitmap(bmp);
  481.  
  482.   if(GetPrivateProfileString(name, "over", "", buffer, 2048, skinFile)) {
  483.     CString overBmp = path + buffer;
  484.  
  485.     bmp = LoadImage(overBmp);
  486.  
  487.     if (!bmp) {
  488.       m_error = "Error loading button over bitmap " + overBmp;
  489.       return false;
  490.     }
  491.     m_buttons[num].SetOverBitmap(bmp);
  492.   }
  493.  
  494.   if(GetPrivateProfileString(name, "region", "", buffer, 2048, skinFile)) {
  495.     CString region = path + buffer;
  496.     
  497.     HRGN rgn = LoadRegion(region);
  498.     if(!rgn) {
  499.       m_error = "Error loading button region " + region;
  500.       return false;
  501.     }
  502.     m_buttons[num].SetRegion(rgn);
  503.   }
  504.  
  505.   if(!GetPrivateProfileString(name, "id", "", buffer, 2048, skinFile)) {
  506.     "Missing button ID for " + name;
  507.     return false;
  508.   }
  509.   m_buttons[num].SetId(buffer);
  510.  
  511.   if(!GetPrivateProfileString(name, "rect", "", buffer, 2048, skinFile)) {
  512.     m_error = "Missing button rectangle for " + name;
  513.     return false;
  514.   }
  515.   
  516.   RECT r;
  517.   if(!ParseRect(buffer, r)) {
  518.     m_error = "Invalid button rectangle for " + name;
  519.     return false;
  520.   }
  521.   m_buttons[num].SetRect(r);
  522.  
  523.   return true;
  524. }
  525.  
  526. // ------------------------------------------------------------------------
  527. // Default skin window procedure.
  528. // Here the class will handle WM_PAINT and WM_LBUTTONDOWN, originally sent
  529. // to the application window, but now subclassed. Any other messages will
  530. // just pass through the procedure and reach the original app procedure.
  531. // ------------------------------------------------------------------------
  532. LRESULT CALLBACK SkinWndProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
  533. {
  534.   // we will need a pointer to the associated class instance
  535.   // (it was stored in the window before, remember?)
  536.   CSkin *pSkin = (CSkin*)GetProp(hWnd, "skin");
  537.  
  538.   // to handle WM_PAINT
  539.   PAINTSTRUCT ps;
  540.  
  541.   // if we fail to get our class instance, we can't handle anything.
  542.   if (!pSkin) return DefWindowProc(hWnd,uMessage,wParam,lParam);
  543.  
  544.   switch(uMessage)
  545.     {
  546.     case WM_WINDOWPOSCHANGING:
  547.       {
  548.         LPWINDOWPOS pos = (LPWINDOWPOS)lParam;
  549.         pos->cx = pSkin->Width();
  550.         pos->cy = pSkin->Height();
  551.         return 0L;
  552.       }
  553.       break;
  554.  
  555.     case WM_PAINT:
  556.       {
  557.         // ---------------------------------------------------------
  558.         // here we just need to blit our skin
  559.         // directly to the device context
  560.         // passed by the painting message.
  561.         // ---------------------------------------------------------
  562.         BeginPaint(hWnd,&ps);
  563.  
  564.         // blit the skin
  565.         BitBlt(ps.hdc,0,0,pSkin->Width(),pSkin->Height(),pSkin->HDC(),0,0,SRCCOPY);
  566.  
  567.         EndPaint(hWnd,&ps);
  568.         break;
  569.       }
  570.  
  571.     case WM_LBUTTONDOWN:
  572.       {
  573.         // ---------------------------------------------------------
  574.         // this is a common trick for easy dragging of the window.
  575.         // this message fools windows telling that the user is
  576.         // actually dragging the application caption bar.
  577.         // ---------------------------------------------------------
  578.         SendMessage(hWnd, WM_NCLBUTTONDOWN, HTCAPTION,NULL);
  579.         break;
  580.       }
  581.  
  582.     }
  583.  
  584.   // ---------------------------------------------------------
  585.   // call the default window procedure to keep things going.
  586.   // ---------------------------------------------------------
  587.   return CallWindowProc(pSkin->m_OldWndProc, hWnd, uMessage, wParam, lParam);
  588. }
  589.