1   /************************************************************************
  2           Copy Handler 1.x - program for copying data in Microsoft Windows
  3                                                    systems.
  4           Copyright (C) 2001-2004 Ixen Gerthannes (copyhandler@o2.pl)
  5  
  6           This program is free software; you can redistribute it and/or modify
  7           it under the terms of the GNU General Public License as published by
  8           the Free Software Foundation; either version 2 of the License, or
  9           (at your option) any later version.
  10  
  11           This program is distributed in the hope that it will be useful,
  12           but WITHOUT ANY WARRANTY; without even the implied warranty of
  13           MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14           GNU General Public License for more details.
  15  
  16           You should have received a copy of the GNU General Public License
  17           along with this program; if not, write to the Free Software
  18           Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  19   *************************************************************************/
  20   #include "stdafx.h"
  21   #include "StaticEx.h"
  22  
  23   #define STATICEX_CLASS _T("STATICEX")
  24  
  25   LRESULT CALLBACK StaticExWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  26   {
  27           STATICEXSETTINGS* pSettings=(STATICEXSETTINGS*)GetWindowLongPtr(hwnd, 0);
  28           switch (uMsg)
  29           {
  30           case WM_NCCREATE:
  31                   {
  32                           STATICEXSETTINGS* pSett=new STATICEXSETTINGS;
  33                           pSett->hFontNormal=NULL;
  34                           pSett->hFontUnderline=NULL;
  35                           pSett->pszLink=NULL;
  36                           pSett->pszText=NULL;
  37                           pSett->bActive=false;
  38                           pSett->bDown=false;
  39                           pSett->hLink=NULL;
  40                           pSett->hNormal=NULL;
  41                           pSett->rcText.left=0;
  42                           pSett->rcText.right=0;
  43                           pSett->rcText.top=0;
  44                           pSett->rcText.bottom=0;
  45                           ::SetWindowLongPtr(hwnd, 0, (LONG_PTR)pSett);
  46  
  47                           // create cursors
  48                           pSett->hNormal=::LoadCursor(NULL, IDC_ARROW);
  49                           pSett->hLink=::LoadCursor(NULL, IDC_HAND);
  50  
  51                           break;
  52                   }
  53           case WM_CREATE:
  54                   {
  55                           CREATESTRUCT* pcs=(CREATESTRUCT*)lParam;
  56  
  57                           TCHAR* pSep=_tcsrchr(pcs->lpszName, _T('|'));
  58  
  59                           if (!(pcs->style & SES_LINK) || pSep == NULL || pSep-pcs->lpszName < 0)
  60                           {
  61                                   pSettings->pszText=new TCHAR[_tcslen(pcs->lpszName)+1];
  62                                   _tcscpy(pSettings->pszText, pcs->lpszName);
  63                                   pSettings->pszLink=NULL;
  64                           }
  65                           else
  66                           {
  67                                   pSettings->pszText=new TCHAR[pSep-pcs->lpszName+1];
  68                                   _tcsncpy(pSettings->pszText, pcs->lpszName, pSep-pcs->lpszName);
  69                                   pSettings->pszText[pSep-pcs->lpszName]=_T('\0');
  70                                   pSep++;
  71                                   pSettings->pszLink=new TCHAR[_tcslen(pSep)+1];
  72                                   _tcscpy(pSettings->pszLink, pSep);
  73                           }
  74  
  75                           break;
  76                   }
  77           case WM_NCDESTROY:
  78                   {
  79                           if (pSettings->hFontNormal)
  80                                   DeleteObject(pSettings->hFontNormal);
  81                           if (pSettings->hFontUnderline)
  82                                   DeleteObject(pSettings->hFontUnderline);
  83                           if (pSettings->hLink)
  84                                   DeleteObject(pSettings->hLink);
  85                           if (pSettings->hNormal)
  86                                   DeleteObject(pSettings->hNormal);
  87                           delete [] pSettings->pszLink;
  88                           delete [] pSettings->pszText;
  89  
  90                           delete pSettings;
  91                           break;
  92                   }
  93           case WM_SETFONT:
  94                   {
  95                           // delete old fonts
  96                           if (pSettings->hFontNormal)
  97                                   DeleteObject(pSettings->hFontNormal);
  98                           if (pSettings->hFontUnderline)
  99                                   DeleteObject(pSettings->hFontUnderline);
  100  
  101                           // new font - create a font based on it (the normal and the underlined one)
  102                           HFONT hfont=(HFONT)wParam;
  103                           LOGFONT lf;
  104                           if (GetObject(hfont, sizeof(LOGFONT), &lf) != 0)
  105                           {
  106                                   // size
  107                                   if (::GetWindowLong(hwnd, GWL_STYLE) & SES_LARGE)
  108                                           lf.lfHeight=(long)(lf.lfHeight*1.25);
  109  
  110                                   // create a font
  111                                   if (::GetWindowLong(hwnd, GWL_STYLE) & SES_BOLD)
  112                                           lf.lfWeight=FW_BOLD;
  113                                   pSettings->hFontNormal=CreateFontIndirect(&lf);
  114                                   lf.lfUnderline=TRUE;
  115                                   pSettings->hFontUnderline=CreateFontIndirect(&lf);
  116                           }
  117                           else
  118                           {
  119                                   pSettings->hFontNormal=NULL;
  120                                   pSettings->hFontUnderline=NULL;
  121                           }
  122  
  123                           break;
  124                   }
  125           case WM_SETTEXT:
  126                   {
  127                           // delete the old font
  128                           delete [] pSettings->pszText;
  129                           delete [] pSettings->pszLink;
  130  
  131                           // style
  132                           LONG lStyle=::GetWindowLong(hwnd, GWL_STYLE);
  133  
  134                           LPCTSTR psz=(LPCTSTR)lParam;
  135                           TCHAR* pSep=_tcsrchr(psz, _T('|'));
  136  
  137                           if (!(lStyle & SES_LINK) || pSep == NULL || pSep-psz < 0)
  138                           {
  139                                   pSettings->pszText=new TCHAR[_tcslen(psz)+1];
  140                                   _tcscpy(pSettings->pszText, psz);
  141                                   pSettings->pszLink=NULL;
  142                           }
  143                           else
  144                           {
  145                                   pSettings->pszText=new TCHAR[pSep-psz+1];
  146                                   _tcsncpy(pSettings->pszText, psz, pSep-psz);
  147                                   pSettings->pszText[pSep-psz]=_T('\0');
  148                                   pSep++;
  149                                   pSettings->pszLink=new TCHAR[_tcslen(pSep)+1];
  150                                   _tcscpy(pSettings->pszLink, pSep);
  151                           }
  152  
  153                           ::RedrawWindow(hwnd, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE);
  154                           break;
  155                   }
  156           case WM_ERASEBKGND:
  157                   {
  158                           return (LRESULT)FALSE;
  159                           break;
  160                   }
  161           case WM_PAINT:
  162                   {
  163                           // draw anything
  164                           PAINTSTRUCT ps;
  165                           HDC hDC=BeginPaint(hwnd, &ps);
  166  
  167                           // flicker-free drawing
  168                           HDC hdc=::CreateCompatibleDC(hDC);
  169                           HBITMAP hBmp=::CreateCompatibleBitmap(hDC, ps.rcPaint.right-ps.rcPaint.left+1, ps.rcPaint.bottom-ps.rcPaint.top+1);
  170                           HBITMAP hOldBitmap=(HBITMAP)::SelectObject(hdc, hBmp);
  171                           ::SetWindowOrgEx(hdc, ps.rcPaint.left, ps.rcPaint.top, NULL);
  172  
  173                           // paint the background
  174                           ::FillRect(hdc, &ps.rcPaint, (HBRUSH)::SendMessage((HWND)::GetWindowLong(hwnd, GWL_HWNDPARENT), WM_CTLCOLORSTATIC, (WPARAM)hdc, (LPARAM)hwnd));
  175  
  176                           // size of the all control
  177                           RECT rcCtl;
  178                           ::GetClientRect(hwnd, &rcCtl);
  179  
  180                           // draw text
  181                           DWORD dwFlags=DT_LEFT | DT_VCENTER | (::GetWindowLong(hwnd, GWL_STYLE) & SES_PATHELLIPSIS ? DT_PATH_ELLIPSIS : 0)
  182                                   | (::GetWindowLong(hwnd, GWL_STYLE) & SES_ELLIPSIS ? DT_END_ELLIPSIS : 0)
  183                                   | (::GetWindowLong(hwnd, GWL_STYLE) & SES_WORDBREAK ? DT_WORDBREAK : 0);
  184  
  185                           pSettings->rcText=rcCtl;
  186                           if (::GetWindowLong(hwnd, GWL_STYLE) & SES_LINK)
  187                           {
  188                                   HFONT hOld=(HFONT)::SelectObject(hdc, pSettings->hFontUnderline);
  189                                   ::SetBkMode(hdc, TRANSPARENT);
  190  
  191                                   COLORREF crColor=(pSettings->bActive ? (RGB(255, 0, 0)) : (RGB(0, 0, 255)));
  192                                   ::SetTextColor(hdc, crColor);
  193  
  194                                   if (pSettings->pszText)
  195                                   {
  196                                           DrawText(hdc, pSettings->pszText, -1, &pSettings->rcText, dwFlags | DT_CALCRECT);
  197                                           DrawText(hdc, pSettings->pszText, -1, &rcCtl, dwFlags);
  198                                   }
  199                                   else
  200                                   {
  201                                           pSettings->rcText.left=0;
  202                                           pSettings->rcText.right=0;
  203                                           pSettings->rcText.top=0;
  204                                           pSettings->rcText.bottom=0;
  205                                   }
  206  
  207                                   ::SelectObject(hdc, hOld);
  208                           }
  209                           else
  210                           {
  211                                   // aesthetics
  212                                   rcCtl.left+=3;
  213                                   rcCtl.right-=3;
  214  
  215                                   // draw
  216                                   HFONT hOld=(HFONT)::SelectObject(hdc, pSettings->hFontNormal);
  217                                   ::SetBkMode(hdc, TRANSPARENT);
  218                                   ::SetTextColor(hdc, ::GetSysColor(COLOR_BTNTEXT));
  219  
  220                                   if (pSettings->pszText)
  221                                   {
  222                                           DWORD dwMod=(::GetWindowLong(hwnd, GWL_STYLE) & SES_RALIGN) ? DT_RIGHT : 0;
  223                                           DrawText(hdc, pSettings->pszText, -1, &pSettings->rcText, dwFlags | DT_CALCRECT | dwMod);
  224                                           DrawText(hdc, pSettings->pszText, -1, &rcCtl, dwFlags | dwMod);
  225                                   }
  226                                   else
  227                                   {
  228                                           pSettings->rcText.left=0;
  229                                           pSettings->rcText.right=0;
  230                                           pSettings->rcText.top=0;
  231                                           pSettings->rcText.bottom=0;
  232                                   }
  233  
  234                                   ::SelectObject(hdc, hOld);
  235                           }
  236  
  237                           // free the compatible dc
  238                           ::BitBlt(ps.hdc, ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right-ps.rcPaint.left+1, ps.rcPaint.bottom-ps.rcPaint.top+1,
  239                                   hdc, ps.rcPaint.left, ps.rcPaint.top, SRCCOPY);
  240                           ::SelectObject(hdc, hOldBitmap);
  241                           ::DeleteObject(hBmp);
  242                           ::DeleteDC(hdc);
  243  
  244                           EndPaint(hwnd, &ps);
  245  
  246                           break;
  247                   }
  248           case WM_MOUSEMOVE:
  249                   {
  250                           if (::GetWindowLong(hwnd, GWL_STYLE) & SES_LINK)
  251                           {
  252                                   POINT pt = { LOWORD(lParam), HIWORD(lParam) };
  253                                   
  254                                   if (pSettings->bActive)
  255                                   {
  256                                           if (!::PtInRect(&pSettings->rcText, pt))
  257                                           {
  258                                                   pSettings->bActive=false;
  259                                                   ::ReleaseCapture();
  260                                                   ::RedrawWindow(hwnd, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE);
  261                                                   
  262                                                   ::SetCursor(pSettings->hNormal);
  263                                           }
  264                                   }
  265                                   else
  266                                   {
  267                                           if (::PtInRect(&pSettings->rcText, pt))
  268                                           {
  269                                                   pSettings->bActive=true;
  270                                                   ::RedrawWindow(hwnd, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE);
  271                                                   ::SetCapture(hwnd);
  272                                                   ::SetCursor(pSettings->hLink);
  273                                           }
  274                                   }
  275                           }
  276                           break;
  277                   }
  278           case WM_LBUTTONDOWN:
  279                   {
  280                           pSettings->bDown=true;
  281                           break;
  282                   }
  283           case WM_LBUTTONUP:
  284                   {
  285                           POINT pt={ LOWORD(lParam), HIWORD(lParam) };
  286                           if (pSettings->bDown && ::GetWindowLong(hwnd, GWL_STYLE) & SES_LINK && ::PtInRect(&pSettings->rcText, pt))
  287                           {
  288                                   if (::GetWindowLong(hwnd, GWL_STYLE) & SES_NOTIFY)
  289                                   {
  290                                           ::SendMessage((HWND)::GetWindowLong(hwnd, GWL_HWNDPARENT), WM_COMMAND, (WPARAM)(SEN_CLICKED << 16 | ::GetWindowLong(hwnd, GWL_ID)), (LPARAM)hwnd);
  291                                   }
  292                                   else
  293                                   {
  294                                           
  295                                           TRACE("Executing %s...\n", pSettings->pszLink);
  296                                           ShellExecute(NULL, "open", pSettings->pszLink, NULL, NULL, SW_SHOWNORMAL);
  297                                   }
  298                           }
  299                           pSettings->bDown=false;
  300  
  301                           break;
  302                   }
  303           case WM_CANCELMODE:
  304                   {
  305                           pSettings->bActive=false;
  306                           pSettings->bDown=false;
  307                           break;
  308                   }
  309           case SEM_GETLINK:
  310                   {
  311                           // wParam - count
  312                           // lParam - addr of a buffer
  313                           if (pSettings->pszLink)
  314                                   _tcsncpy((PTSTR)lParam, pSettings->pszLink, (int)wParam);
  315                           else
  316                                   _tcscpy((PTSTR)lParam, _T(""));
  317  
  318                           return (LRESULT)TRUE;
  319                           break;
  320                   }
  321           }
  322  
  323           return ::DefWindowProc(hwnd, uMsg, wParam, lParam);
  324   }
  325  
  326   bool RegisterStaticExControl(HINSTANCE hInstance)
  327   {
  328       WNDCLASS wndcls;
  329  
  330       if (!(::GetClassInfo(hInstance, STATICEX_CLASS, &wndcls)))
  331       {
  332           // need to register a new class
  333           wndcls.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
  334                   wndcls.lpfnWndProc = ::StaticExWndProc;
  335           wndcls.cbClsExtra = 0;
  336                   wndcls.cbWndExtra = sizeof(STATICEXSETTINGS*);
  337           wndcls.hInstance = hInstance;
  338           wndcls.hIcon = NULL;
  339                   wndcls.hCursor = NULL;                          // will load each time needed
  340           wndcls.hbrBackground = NULL;
  341           wndcls.lpszMenuName = NULL;
  342           wndcls.lpszClassName = STATICEX_CLASS;
  343  
  344           if (!RegisterClass(&wndcls))
  345               return false;
  346       }
  347  
  348       return true;
  349   }