ReactOS Fundraising Campaign 2012
 
€ 4,410 / € 30,000

Information | Donate

Home | Info | Community | Development | myReactOS | Contact Us

  1. Home
  2. Community
  3. Development
  4. myReactOS
  5. Fundraiser 2012

  1. Main Page
  2. Alphabetical List
  3. Data Structures
  4. Directories
  5. File List
  6. Data Fields
  7. Globals
  8. Related Pages

ReactOS Development > Doxygen

syslink.c
Go to the documentation of this file.
00001 /*
00002  * SysLink control
00003  *
00004  * Copyright 2004 - 2006 Thomas Weidenmueller <w3seek@reactos.com>
00005  *
00006  * This library is free software; you can redistribute it and/or
00007  * modify it under the terms of the GNU Lesser General Public
00008  * License as published by the Free Software Foundation; either
00009  * version 2.1 of the License, or (at your option) any later version.
00010  *
00011  * This library is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014  * Lesser General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU Lesser General Public
00017  * License along with this library; if not, write to the Free Software
00018  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
00019  *
00020  * NOTES
00021  *
00022  * This code was audited for completeness against the documented features
00023  * of Comctl32.dll version 6.0 on Apr. 4, 2005, by Dimitrie O. Paun.
00024  * 
00025  * Unless otherwise noted, we believe this code to be complete, as per
00026  * the specification mentioned above.
00027  * If you discover missing features, or bugs, please note them below.
00028  */
00029 
00030 #include <stdarg.h>
00031 #include <string.h>
00032 #include "windef.h"
00033 #include "winbase.h"
00034 #include "wingdi.h"
00035 #include "winuser.h"
00036 #include "winnls.h"
00037 #include "commctrl.h"
00038 #include "comctl32.h"
00039 #include "wine/unicode.h"
00040 #include "wine/debug.h"
00041 
00042 WINE_DEFAULT_DEBUG_CHANNEL(syslink);
00043 
00044 INT WINAPI StrCmpNIW(LPCWSTR,LPCWSTR,INT);
00045 
00046 typedef struct
00047 {
00048     int nChars;
00049     int nSkip;
00050     RECT rc;
00051 } DOC_TEXTBLOCK, *PDOC_TEXTBLOCK;
00052 
00053 #define LIF_FLAGSMASK   (LIF_STATE | LIF_ITEMID | LIF_URL)
00054 #define LIS_MASK        (LIS_FOCUSED | LIS_ENABLED | LIS_VISITED)
00055 
00056 typedef enum
00057 {
00058     slText = 0,
00059     slLink
00060 } SL_ITEM_TYPE;
00061 
00062 typedef struct _DOC_ITEM
00063 {
00064     struct _DOC_ITEM *Next; /* Address to the next item */
00065     UINT nText;             /* Number of characters of the text */
00066     SL_ITEM_TYPE Type;      /* type of the item */
00067     PDOC_TEXTBLOCK Blocks;  /* Array of text blocks */
00068     union
00069     {
00070         struct
00071         {
00072             UINT state;     /* Link state */
00073             WCHAR *szID;    /* Link ID string */
00074             WCHAR *szUrl;   /* Link URL string */
00075         } Link;
00076         struct
00077         {
00078             UINT Dummy;
00079         } Text;
00080     } u;
00081     WCHAR Text[1];          /* Text of the document item */
00082 } DOC_ITEM, *PDOC_ITEM;
00083 
00084 typedef struct
00085 {
00086     HWND      Self;         /* The window handle for this control */
00087     HWND      Notify;       /* The parent handle to receive notifications */
00088     DWORD     Style;        /* Styles for this control */
00089     PDOC_ITEM Items;        /* Address to the first document item */
00090     BOOL      HasFocus;     /* Whether the control has the input focus */
00091     int       MouseDownID;  /* ID of the link that the mouse button first selected */
00092     HFONT     Font;         /* Handle to the font for text */
00093     HFONT     LinkFont;     /* Handle to the font for links */
00094     COLORREF  TextColor;    /* Color of the text */
00095     COLORREF  LinkColor;    /* Color of links */
00096     COLORREF  VisitedColor; /* Color of visited links */
00097     WCHAR     BreakChar;    /* Break Character for the current font */
00098     BOOL      IgnoreReturn; /* (infoPtr->Style & LWS_IGNORERETURN) on creation */
00099 } SYSLINK_INFO;
00100 
00101 static const WCHAR SL_LINKOPEN[] =  { '<','a', 0 };
00102 static const WCHAR SL_HREF[] =      { 'h','r','e','f','=','\"',0 };
00103 static const WCHAR SL_ID[] =        { 'i','d','=','\"',0 };
00104 static const WCHAR SL_LINKCLOSE[] = { '<','/','a','>',0 };
00105 
00106 /* Control configuration constants */
00107 
00108 #define SL_LEFTMARGIN   (0)
00109 #define SL_TOPMARGIN    (0)
00110 #define SL_RIGHTMARGIN  (0)
00111 #define SL_BOTTOMMARGIN (0)
00112 
00113 /***********************************************************************
00114  * SYSLINK_FreeDocItem
00115  * Frees all data and gdi objects associated with a document item
00116  */
00117 static VOID SYSLINK_FreeDocItem (PDOC_ITEM DocItem)
00118 {
00119     if(DocItem->Type == slLink)
00120     {
00121         Free(DocItem->u.Link.szID);
00122         Free(DocItem->u.Link.szUrl);
00123     }
00124 
00125     /* we don't free Text because it's just a pointer to a character in the
00126        entire window text string */
00127 
00128     Free(DocItem);
00129 }
00130 
00131 /***********************************************************************
00132  * SYSLINK_AppendDocItem
00133  * Create and append a new document item.
00134  */
00135 static PDOC_ITEM SYSLINK_AppendDocItem (SYSLINK_INFO *infoPtr, LPCWSTR Text, UINT textlen,
00136                                         SL_ITEM_TYPE type, PDOC_ITEM LastItem)
00137 {
00138     PDOC_ITEM Item;
00139 
00140     textlen = min(textlen, strlenW(Text));
00141     Item = Alloc(FIELD_OFFSET(DOC_ITEM, Text[textlen + 1]));
00142     if(Item == NULL)
00143     {
00144         ERR("Failed to alloc DOC_ITEM structure!\n");
00145         return NULL;
00146     }
00147 
00148     Item->Next = NULL;
00149     Item->nText = textlen;
00150     Item->Type = type;
00151     Item->Blocks = NULL;
00152     
00153     if(LastItem != NULL)
00154     {
00155         LastItem->Next = Item;
00156     }
00157     else
00158     {
00159         infoPtr->Items = Item;
00160     }
00161     
00162     lstrcpynW(Item->Text, Text, textlen + 1);
00163     
00164     return Item;
00165 }
00166 
00167 /***********************************************************************
00168  * SYSLINK_ClearDoc
00169  * Clears the document tree
00170  */
00171 static VOID SYSLINK_ClearDoc (SYSLINK_INFO *infoPtr)
00172 {
00173     PDOC_ITEM Item, Next;
00174     
00175     Item = infoPtr->Items;
00176     while(Item != NULL)
00177     {
00178         Next = Item->Next;
00179         SYSLINK_FreeDocItem(Item);
00180         Item = Next;
00181     }
00182     
00183     infoPtr->Items = NULL;
00184 }
00185 
00186 /***********************************************************************
00187  * SYSLINK_ParseText
00188  * Parses the window text string and creates a document. Returns the
00189  * number of document items created.
00190  */
00191 static UINT SYSLINK_ParseText (SYSLINK_INFO *infoPtr, LPCWSTR Text)
00192 {
00193     LPCWSTR current, textstart = NULL, linktext = NULL, firsttag = NULL;
00194     int taglen = 0, textlen = 0, linklen = 0, docitems = 0;
00195     PDOC_ITEM Last = NULL;
00196     SL_ITEM_TYPE CurrentType = slText;
00197     LPCWSTR lpID, lpUrl;
00198     UINT lenId, lenUrl;
00199 
00200     TRACE("(%p %s)\n", infoPtr, debugstr_w(Text));
00201 
00202     for(current = Text; *current != 0;)
00203     {
00204         if(*current == '<')
00205         {
00206             if(!StrCmpNIW(current, SL_LINKOPEN, 2) && (CurrentType == slText))
00207             {
00208                 BOOL ValidParam = FALSE, ValidLink = FALSE;
00209 
00210                 if(*(current + 2) == '>')
00211                 {
00212                     /* we just have to deal with a <a> tag */
00213                     taglen = 3;
00214                     ValidLink = TRUE;
00215                     ValidParam = TRUE;
00216                     firsttag = current;
00217                     linklen = 0;
00218                     lpID = NULL;
00219                     lpUrl = NULL;
00220                 }
00221                 else if(*(current + 2) == infoPtr->BreakChar)
00222                 {
00223                     /* we expect parameters, parse them */
00224                     LPCWSTR *CurrentParameter = NULL, tmp;
00225                     UINT *CurrentParameterLen = NULL;
00226 
00227                     taglen = 3;
00228                     tmp = current + taglen;
00229                     lpID = NULL;
00230                     lpUrl = NULL;
00231                     
00232 CheckParameter:
00233                     /* compare the current position with all known parameters */
00234                     if(!StrCmpNIW(tmp, SL_HREF, 6))
00235                     {
00236                         taglen += 6;
00237                         ValidParam = TRUE;
00238                         CurrentParameter = &lpUrl;
00239                         CurrentParameterLen = &lenUrl;
00240                     }
00241                     else if(!StrCmpNIW(tmp, SL_ID, 4))
00242                     {
00243                         taglen += 4;
00244                         ValidParam = TRUE;
00245                         CurrentParameter = &lpID;
00246                         CurrentParameterLen = &lenId;
00247                     }
00248                     else
00249                     {
00250                         ValidParam = FALSE;
00251                     }
00252                     
00253                     if(ValidParam)
00254                     {
00255                         /* we got a known parameter, now search until the next " character.
00256                            If we can't find a " character, there's a syntax error and we just assume it's text */
00257                         ValidParam = FALSE;
00258                         *CurrentParameter = current + taglen;
00259                         *CurrentParameterLen = 0;
00260 
00261                         for(tmp = *CurrentParameter; *tmp != 0; tmp++)
00262                         {
00263                             taglen++;
00264                             if(*tmp == '\"')
00265                             {
00266                                 ValidParam = TRUE;
00267                                 tmp++;
00268                                 break;
00269                             }
00270                             (*CurrentParameterLen)++;
00271                         }
00272                     }
00273                     if(ValidParam)
00274                     {
00275                         /* we're done with this parameter, now there are only 2 possibilities:
00276                          * 1. another parameter is coming, so expect a ' ' (space) character
00277                          * 2. the tag is being closed, so expect a '<' character
00278                          */
00279                         if(*tmp == infoPtr->BreakChar)
00280                         {
00281                             /* we expect another parameter, do the whole thing again */
00282                             taglen++;
00283                             tmp++;
00284                             goto CheckParameter;
00285                         }
00286                         else if(*tmp == '>')
00287                         {
00288                             /* the tag is being closed, we're done */
00289                             ValidLink = TRUE;
00290                             taglen++;
00291                         }
00292                     }
00293                 }
00294                 
00295                 if(ValidLink && ValidParam)
00296                 {
00297                     /* the <a ...> tag appears to be valid. save all information
00298                        so we can add the link if we find a valid </a> tag later */
00299                     CurrentType = slLink;
00300                     linktext = current + taglen;
00301                     linklen = 0;
00302                     firsttag = current;
00303                 }
00304                 else
00305                 {
00306                     taglen = 1;
00307                     lpID = NULL;
00308                     lpUrl = NULL;
00309                     if(textstart == NULL)
00310                     {
00311                         textstart = current;
00312                     }
00313                 }
00314             }
00315             else if(!StrCmpNIW(current, SL_LINKCLOSE, 4) && (CurrentType == slLink) && firsttag)
00316             {
00317                 /* there's a <a...> tag opened, first add the previous text, if present */
00318                 if(textstart != NULL && textlen > 0 && firsttag > textstart)
00319                 {
00320                     Last = SYSLINK_AppendDocItem(infoPtr, textstart, firsttag - textstart, slText, Last);
00321                     if(Last == NULL)
00322                     {
00323                         ERR("Unable to create new document item!\n");
00324                         return docitems;
00325                     }
00326                     docitems++;
00327                     textstart = NULL;
00328                     textlen = 0;
00329                 }
00330                 
00331                 /* now it's time to add the link to the document */
00332                 current += 4;
00333                 if(linktext != NULL && linklen > 0)
00334                 {
00335                     Last = SYSLINK_AppendDocItem(infoPtr, linktext, linklen, slLink, Last);
00336                     if(Last == NULL)
00337                     {
00338                         ERR("Unable to create new document item!\n");
00339                         return docitems;
00340                     }
00341                     docitems++;
00342                     if(CurrentType == slLink)
00343                     {
00344                         int nc;
00345 
00346                         if(!(infoPtr->Style & WS_DISABLED))
00347                         {
00348                             Last->u.Link.state |= LIS_ENABLED;
00349                         }
00350                         /* Copy the tag parameters */
00351                         if(lpID != NULL)
00352                         {
00353                             nc = min(lenId, strlenW(lpID));
00354                             nc = min(nc, MAX_LINKID_TEXT - 1);
00355                             Last->u.Link.szID = Alloc((nc + 1) * sizeof(WCHAR));
00356                             if(Last->u.Link.szID != NULL)
00357                             {
00358                                 lstrcpynW(Last->u.Link.szID, lpID, nc + 1);
00359                             }
00360                         }
00361                         else
00362                             Last->u.Link.szID = NULL;
00363                         if(lpUrl != NULL)
00364                         {
00365                             nc = min(lenUrl, strlenW(lpUrl));
00366                             nc = min(nc, L_MAX_URL_LENGTH - 1);
00367                             Last->u.Link.szUrl = Alloc((nc + 1) * sizeof(WCHAR));
00368                             if(Last->u.Link.szUrl != NULL)
00369                             {
00370                                 lstrcpynW(Last->u.Link.szUrl, lpUrl, nc + 1);
00371                             }
00372                         }
00373                         else
00374                             Last->u.Link.szUrl = NULL;
00375                     }
00376                     linktext = NULL;
00377                 }
00378                 CurrentType = slText;
00379                 firsttag = NULL;
00380                 textstart = NULL;
00381                 continue;
00382             }
00383             else
00384             {
00385                 /* we don't know what tag it is, so just continue */
00386                 taglen = 1;
00387                 linklen++;
00388                 if(CurrentType == slText && textstart == NULL)
00389                 {
00390                     textstart = current;
00391                 }
00392             }
00393             
00394             textlen += taglen;
00395             current += taglen;
00396         }
00397         else
00398         {
00399             textlen++;
00400             linklen++;
00401 
00402             /* save the pointer of the current text item if we couldn't find a tag */
00403             if(textstart == NULL && CurrentType == slText)
00404             {
00405                 textstart = current;
00406             }
00407             
00408             current++;
00409         }
00410     }
00411     
00412     if(textstart != NULL && textlen > 0)
00413     {
00414         Last = SYSLINK_AppendDocItem(infoPtr, textstart, textlen, CurrentType, Last);
00415         if(Last == NULL)
00416         {
00417             ERR("Unable to create new document item!\n");
00418             return docitems;
00419         }
00420         if(CurrentType == slLink)
00421         {
00422             int nc;
00423 
00424             if(!(infoPtr->Style & WS_DISABLED))
00425             {
00426                 Last->u.Link.state |= LIS_ENABLED;
00427             }
00428             /* Copy the tag parameters */
00429             if(lpID != NULL)
00430             {
00431                 nc = min(lenId, strlenW(lpID));
00432                 nc = min(nc, MAX_LINKID_TEXT - 1);
00433                 Last->u.Link.szID = Alloc((nc + 1) * sizeof(WCHAR));
00434                 if(Last->u.Link.szID != NULL)
00435                 {
00436                     lstrcpynW(Last->u.Link.szID, lpID, nc + 1);
00437                 }
00438             }
00439             else
00440                 Last->u.Link.szID = NULL;
00441             if(lpUrl != NULL)
00442             {
00443                 nc = min(lenUrl, strlenW(lpUrl));
00444                 nc = min(nc, L_MAX_URL_LENGTH - 1);
00445                 Last->u.Link.szUrl = Alloc((nc + 1) * sizeof(WCHAR));
00446                 if(Last->u.Link.szUrl != NULL)
00447                 {
00448                     lstrcpynW(Last->u.Link.szUrl, lpUrl, nc + 1);
00449                 }
00450             }
00451             else
00452                 Last->u.Link.szUrl = NULL;
00453         }
00454         docitems++;
00455     }
00456 
00457     if(linktext != NULL && linklen > 0)
00458     {
00459         /* we got an unclosed link, just display the text */
00460         Last = SYSLINK_AppendDocItem(infoPtr, linktext, linklen, slText, Last);
00461         if(Last == NULL)
00462         {
00463             ERR("Unable to create new document item!\n");
00464             return docitems;
00465         }
00466         docitems++;
00467     }
00468 
00469     return docitems;
00470 }
00471 
00472 /***********************************************************************
00473  * SYSLINK_RepaintLink
00474  * Repaints a link.
00475  */
00476 static VOID SYSLINK_RepaintLink (const SYSLINK_INFO *infoPtr, const DOC_ITEM *DocItem)
00477 {
00478     PDOC_TEXTBLOCK bl;
00479     int n;
00480 
00481     if(DocItem->Type != slLink)
00482     {
00483         ERR("DocItem not a link!\n");
00484         return;
00485     }
00486     
00487     bl = DocItem->Blocks;
00488     if (bl != NULL)
00489     {
00490         n = DocItem->nText;
00491         
00492         while(n > 0)
00493         {
00494             InvalidateRect(infoPtr->Self, &bl->rc, TRUE);
00495             n -= bl->nChars + bl->nSkip;
00496             bl++;
00497         }
00498     }
00499 }
00500 
00501 /***********************************************************************
00502  * SYSLINK_GetLinkItemByIndex
00503  * Retrieves a document link by its index
00504  */
00505 static PDOC_ITEM SYSLINK_GetLinkItemByIndex (const SYSLINK_INFO *infoPtr, int iLink)
00506 {
00507     PDOC_ITEM Current = infoPtr->Items;
00508 
00509     while(Current != NULL)
00510     {
00511         if((Current->Type == slLink) && (iLink-- <= 0))
00512         {
00513             return Current;
00514         }
00515         Current = Current->Next;
00516     }
00517     return NULL;
00518 }
00519 
00520 /***********************************************************************
00521  * SYSLINK_GetFocusLink
00522  * Retrieves the link that has the LIS_FOCUSED bit
00523  */
00524 static PDOC_ITEM SYSLINK_GetFocusLink (const SYSLINK_INFO *infoPtr, int *LinkId)
00525 {
00526     PDOC_ITEM Current = infoPtr->Items;
00527     int id = 0;
00528 
00529     while(Current != NULL)
00530     {
00531         if(Current->Type == slLink)
00532         {
00533             if(Current->u.Link.state & LIS_FOCUSED)
00534             {
00535                 if(LinkId != NULL)
00536                     *LinkId = id;
00537                 return Current;
00538             }
00539             id++;
00540         }
00541         Current = Current->Next;
00542     }
00543     return NULL;
00544 }
00545 
00546 /***********************************************************************
00547  * SYSLINK_GetNextLink
00548  * Gets the next link
00549  */
00550 static PDOC_ITEM SYSLINK_GetNextLink (const SYSLINK_INFO *infoPtr, PDOC_ITEM Current)
00551 {
00552     for(Current = (Current != NULL ? Current->Next : infoPtr->Items);
00553         Current != NULL;
00554         Current = Current->Next)
00555     {
00556         if(Current->Type == slLink)
00557         {
00558             return Current;
00559         }
00560     }
00561     return NULL;
00562 }
00563 
00564 /***********************************************************************
00565  * SYSLINK_GetPrevLink
00566  * Gets the previous link
00567  */
00568 static PDOC_ITEM SYSLINK_GetPrevLink (const SYSLINK_INFO *infoPtr, PDOC_ITEM Current)
00569 {
00570     if(Current == NULL)
00571     {
00572         /* returns the last link */
00573         PDOC_ITEM Last = NULL;
00574         
00575         for(Current = infoPtr->Items; Current != NULL; Current = Current->Next)
00576         {
00577             if(Current->Type == slLink)
00578             {
00579                 Last = Current;
00580             }
00581         }
00582         return Last;
00583     }
00584     else
00585     {
00586         /* returns the previous link */
00587         PDOC_ITEM Cur, Prev = NULL;
00588         
00589         for(Cur = infoPtr->Items; Cur != NULL; Cur = Cur->Next)
00590         {
00591             if(Cur == Current)
00592             {
00593                 break;
00594             }
00595             if(Cur->Type == slLink)
00596             {
00597                 Prev = Cur;
00598             }
00599         }
00600         return Prev;
00601     }
00602 }
00603 
00604 /***********************************************************************
00605  * SYSLINK_WrapLine
00606  * Tries to wrap a line.
00607  */
00608 static BOOL SYSLINK_WrapLine (LPWSTR Text, WCHAR BreakChar, int x, int *LineLen,
00609                              int nFit, LPSIZE Extent)
00610 {
00611     int i;
00612 
00613     for (i = 0; i < nFit; i++) if (Text[i] == '\n') break;
00614 
00615     if (i == *LineLen) return FALSE;
00616 
00617     /* check if we're in the middle of a word */
00618     if (Text[i] != '\n' && Text[i] != BreakChar)
00619     {
00620         /* search for the beginning of the word */
00621         while (i && Text[i - 1] != BreakChar) i--;
00622 
00623         if (i == 0)
00624         {
00625             Extent->cx = 0;
00626             Extent->cy = 0;
00627             if (x == SL_LEFTMARGIN) i = max( nFit, 1 );
00628         }
00629     }
00630     *LineLen = i;
00631     return TRUE;
00632 }
00633 
00634 /***********************************************************************
00635  * SYSLINK_Render
00636  * Renders the document in memory
00637  */
00638 static VOID SYSLINK_Render (const SYSLINK_INFO *infoPtr, HDC hdc, PRECT pRect)
00639 {
00640     RECT rc;
00641     PDOC_ITEM Current;
00642     HGDIOBJ hOldFont;
00643     int x, y, LineHeight;
00644     SIZE szDoc;
00645     TEXTMETRICW tm;
00646 
00647     szDoc.cx = szDoc.cy = 0;
00648 
00649     rc = *pRect;
00650     rc.right -= SL_RIGHTMARGIN;
00651     rc.bottom -= SL_BOTTOMMARGIN;
00652 
00653     if(rc.right - SL_LEFTMARGIN < 0)
00654         rc.right = MAXLONG;
00655     if (rc.bottom - SL_TOPMARGIN < 0)
00656         rc.bottom = MAXLONG;
00657     
00658     hOldFont = SelectObject(hdc, infoPtr->Font);
00659     
00660     x = SL_LEFTMARGIN;
00661     y = SL_TOPMARGIN;
00662     GetTextMetricsW( hdc, &tm );
00663     LineHeight = tm.tmHeight + tm.tmExternalLeading;
00664 
00665     for(Current = infoPtr->Items; Current != NULL; Current = Current->Next)
00666     {
00667         int n, nBlocks;
00668         LPWSTR tx;
00669         PDOC_TEXTBLOCK bl, cbl;
00670         INT nFit;
00671         SIZE szDim;
00672         int SkipChars = 0;
00673 
00674         if(Current->nText == 0)
00675         {
00676             continue;
00677         }
00678 
00679         tx = Current->Text;
00680         n = Current->nText;
00681 
00682         Free(Current->Blocks);
00683         Current->Blocks = NULL;
00684         bl = NULL;
00685         nBlocks = 0;
00686 
00687         if(Current->Type == slText)
00688         {
00689             SelectObject(hdc, infoPtr->Font);
00690         }
00691         else if(Current->Type == slLink)
00692         {
00693             SelectObject(hdc, infoPtr->LinkFont);
00694         }
00695         
00696         while(n > 0)
00697         {
00698             /* skip break characters unless they're the first of the doc item */
00699             if(tx != Current->Text || x == SL_LEFTMARGIN)
00700             {
00701                 if (n && *tx == '\n')
00702                 {
00703                     tx++;
00704                     SkipChars++;
00705                     n--;
00706                 }
00707                 while(n > 0 && (*tx) == infoPtr->BreakChar)
00708                 {
00709                     tx++;
00710                     SkipChars++;
00711                     n--;
00712                 }
00713             }
00714 
00715             if((n == 0 && SkipChars != 0) ||
00716                GetTextExtentExPointW(hdc, tx, n, rc.right - x, &nFit, NULL, &szDim))
00717             {
00718                 int LineLen = n;
00719                 BOOL Wrap = FALSE;
00720                 PDOC_TEXTBLOCK nbl;
00721                 
00722                 if(n != 0)
00723                 {
00724                     Wrap = SYSLINK_WrapLine(tx, infoPtr->BreakChar, x, &LineLen, nFit, &szDim);
00725 
00726                     if(LineLen == 0)
00727                     {
00728                         /* move one line down, the word didn't fit into the line */
00729                         x = SL_LEFTMARGIN;
00730                         y += LineHeight;
00731                         continue;
00732                     }
00733 
00734                     if(LineLen != n)
00735                     {
00736                         if(!GetTextExtentExPointW(hdc, tx, LineLen, rc.right - x, NULL, NULL, &szDim))
00737                         {
00738                             if(bl != NULL)
00739                             {
00740                                 Free(bl);
00741                                 bl = NULL;
00742                                 nBlocks = 0;
00743                             }
00744                             break;
00745                         }
00746                     }
00747                 }
00748                 
00749                 nbl = ReAlloc(bl, (nBlocks + 1) * sizeof(DOC_TEXTBLOCK));
00750                 if (nbl != NULL)
00751                 {
00752                     bl = nbl;
00753                     nBlocks++;
00754 
00755                     cbl = bl + nBlocks - 1;
00756                     
00757                     cbl->nChars = LineLen;
00758                     cbl->nSkip = SkipChars;
00759                     cbl->rc.left = x;
00760                     cbl->rc.top = y;
00761                     cbl->rc.right = x + szDim.cx;
00762                     cbl->rc.bottom = y + szDim.cy;
00763 
00764                     if (cbl->rc.right > szDoc.cx)
00765                         szDoc.cx = cbl->rc.right;
00766                     if (cbl->rc.bottom > szDoc.cy)
00767                         szDoc.cy = cbl->rc.bottom;
00768 
00769                     if(LineLen != 0)
00770                     {
00771                         x += szDim.cx;
00772                         if(Wrap)
00773                         {
00774                             x = SL_LEFTMARGIN;
00775                             y += LineHeight;
00776                         }
00777                     }
00778                 }
00779                 else
00780                 {
00781                     Free(bl);
00782                     bl = NULL;
00783                     nBlocks = 0;
00784 
00785                     ERR("Failed to alloc DOC_TEXTBLOCK structure!\n");
00786                     break;
00787                 }
00788                 n -= LineLen;
00789                 tx += LineLen;
00790                 SkipChars = 0;
00791             }
00792             else
00793             {
00794                 n--;
00795             }
00796         }
00797 
00798         if(nBlocks != 0)
00799         {
00800             Current->Blocks = bl;
00801         }
00802     }
00803     
00804     SelectObject(hdc, hOldFont);
00805 
00806     pRect->right = pRect->left + szDoc.cx;
00807     pRect->bottom = pRect->top + szDoc.cy;
00808 }
00809 
00810 /***********************************************************************
00811  * SYSLINK_Draw
00812  * Draws the SysLink control.
00813  */
00814 static LRESULT SYSLINK_Draw (const SYSLINK_INFO *infoPtr, HDC hdc)
00815 {
00816     RECT rc;
00817     PDOC_ITEM Current;
00818     HFONT hOldFont;
00819     COLORREF OldTextColor, OldBkColor;
00820     HBRUSH hBrush;
00821     UINT text_flags = ETO_CLIPPED;
00822     UINT mode = GetBkMode( hdc );
00823 
00824     hOldFont = SelectObject(hdc, infoPtr->Font);
00825     OldTextColor = SetTextColor(hdc, infoPtr->TextColor);
00826     OldBkColor = SetBkColor(hdc, comctl32_color.clrWindow);
00827 
00828     GetClientRect(infoPtr->Self, &rc);
00829     rc.right -= SL_RIGHTMARGIN + SL_LEFTMARGIN;
00830     rc.bottom -= SL_BOTTOMMARGIN + SL_TOPMARGIN;
00831 
00832     if(rc.right < 0 || rc.bottom < 0) return 0;
00833 
00834     hBrush = (HBRUSH)SendMessageW(infoPtr->Notify, WM_CTLCOLORSTATIC,
00835                                   (WPARAM)hdc, (LPARAM)infoPtr->Self);
00836     if (!(infoPtr->Style & LWS_TRANSPARENT))
00837     {
00838         FillRect(hdc, &rc, hBrush);
00839         if (GetBkMode( hdc ) == OPAQUE) text_flags |= ETO_OPAQUE;
00840     }
00841     else SetBkMode( hdc, TRANSPARENT );
00842 
00843     DeleteObject(hBrush);
00844 
00845     for(Current = infoPtr->Items; Current != NULL; Current = Current->Next)
00846     {
00847         int n;
00848         LPWSTR tx;
00849         PDOC_TEXTBLOCK bl;
00850         
00851         bl = Current->Blocks;
00852         if(bl != NULL)
00853         {
00854             tx = Current->Text;
00855             n = Current->nText;
00856 
00857             if(Current->Type == slText)
00858             {
00859                  SelectObject(hdc, infoPtr->Font);
00860                  SetTextColor(hdc, infoPtr->TextColor);
00861             }
00862             else
00863             {
00864                  SelectObject(hdc, infoPtr->LinkFont);
00865                  SetTextColor(hdc, (!(Current->u.Link.state & LIS_VISITED) ? infoPtr->LinkColor : infoPtr->VisitedColor));
00866             }
00867 
00868             while(n > 0)
00869             {
00870                 tx += bl->nSkip;
00871                 ExtTextOutW(hdc, bl->rc.left, bl->rc.top, text_flags, &bl->rc, tx, bl->nChars, NULL);
00872                 if((Current->Type == slLink) && (Current->u.Link.state & LIS_FOCUSED) && infoPtr->HasFocus)
00873                 {
00874                     COLORREF PrevTextColor;
00875                     PrevTextColor = SetTextColor(hdc, infoPtr->TextColor);
00876                     DrawFocusRect(hdc, &bl->rc);
00877                     SetTextColor(hdc, PrevTextColor);
00878                 }
00879                 tx += bl->nChars;
00880                 n -= bl->nChars + bl->nSkip;
00881                 bl++;
00882             }
00883         }
00884     }
00885 
00886     SetBkColor(hdc, OldBkColor);
00887     SetTextColor(hdc, OldTextColor);
00888     SelectObject(hdc, hOldFont);
00889     SetBkMode(hdc, mode);
00890     return 0;
00891 }
00892 
00893 
00894 /***********************************************************************
00895  * SYSLINK_Paint
00896  * Handles the WM_PAINT message.
00897  */
00898 static LRESULT SYSLINK_Paint (const SYSLINK_INFO *infoPtr, HDC hdcParam)
00899 {
00900     HDC hdc;
00901     PAINTSTRUCT ps;
00902 
00903     hdc = hdcParam ? hdcParam : BeginPaint (infoPtr->Self, &ps);
00904     if (hdc)
00905     {
00906         SYSLINK_Draw (infoPtr, hdc);
00907         if (!hdcParam) EndPaint (infoPtr->Self, &ps);
00908     }
00909     return 0;
00910 }
00911 
00912 /***********************************************************************
00913  *           SYSLINK_SetFont
00914  * Set new Font for the SysLink control.
00915  */
00916 static HFONT SYSLINK_SetFont (SYSLINK_INFO *infoPtr, HFONT hFont, BOOL bRedraw)
00917 {
00918     HDC hdc;
00919     LOGFONTW lf;
00920     TEXTMETRICW tm;
00921     RECT rcClient;
00922     HFONT hOldFont = infoPtr->Font;
00923     infoPtr->Font = hFont;
00924     
00925     /* free the underline font */
00926     if(infoPtr->LinkFont != NULL)
00927     {
00928         DeleteObject(infoPtr->LinkFont);
00929         infoPtr->LinkFont = NULL;
00930     }
00931 
00932     /* Render text position and word wrapping in memory */
00933     if (GetClientRect(infoPtr->Self, &rcClient))
00934     {
00935         hdc = GetDC(infoPtr->Self);
00936         if(hdc != NULL)
00937         {
00938             /* create a new underline font */
00939             if(GetTextMetricsW(hdc, &tm) &&
00940                GetObjectW(infoPtr->Font, sizeof(LOGFONTW), &lf))
00941             {
00942                 lf.lfUnderline = TRUE;
00943                 infoPtr->LinkFont = CreateFontIndirectW(&lf);
00944                 infoPtr->BreakChar = tm.tmBreakChar;
00945             }
00946             else
00947             {
00948                 ERR("Failed to create link font!\n");
00949             }
00950 
00951             SYSLINK_Render(infoPtr, hdc, &rcClient);
00952             ReleaseDC(infoPtr->Self, hdc);
00953         }
00954     }
00955     
00956     if(bRedraw)
00957     {
00958         RedrawWindow(infoPtr->Self, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW);
00959     }
00960     
00961     return hOldFont;
00962 }
00963 
00964 /***********************************************************************
00965  *           SYSLINK_SetText
00966  * Set new text for the SysLink control.
00967  */
00968 static LRESULT SYSLINK_SetText (SYSLINK_INFO *infoPtr, LPCWSTR Text)
00969 {
00970     /* clear the document */
00971     SYSLINK_ClearDoc(infoPtr);
00972 
00973     if(Text == NULL || *Text == 0)
00974     {
00975         return TRUE;
00976     }
00977 
00978     /* let's parse the string and create a document */
00979     if(SYSLINK_ParseText(infoPtr, Text) > 0)
00980     {
00981         RECT rcClient;
00982 
00983         /* Render text position and word wrapping in memory */
00984         if (GetClientRect(infoPtr->Self, &rcClient))
00985         {
00986             HDC hdc = GetDC(infoPtr->Self);
00987             if (hdc != NULL)
00988             {
00989                 SYSLINK_Render(infoPtr, hdc, &rcClient);
00990                 ReleaseDC(infoPtr->Self, hdc);
00991 
00992                 InvalidateRect(infoPtr->Self, NULL, TRUE);
00993             }
00994         }
00995     }
00996     
00997     return TRUE;
00998 }
00999 
01000 /***********************************************************************
01001  *           SYSLINK_SetFocusLink
01002  * Updates the focus status bits and focusses the specified link.
01003  * If no document item is specified, the focus bit will be removed from all links.
01004  * Returns the previous focused item.
01005  */
01006 static PDOC_ITEM SYSLINK_SetFocusLink (const SYSLINK_INFO *infoPtr, const DOC_ITEM *DocItem)
01007 {
01008     PDOC_ITEM Current, PrevFocus = NULL;
01009     
01010     for(Current = infoPtr->Items; Current != NULL; Current = Current->Next)
01011     {
01012         if(Current->Type == slLink)
01013         {
01014             if((PrevFocus == NULL) && (Current->u.Link.state & LIS_FOCUSED))
01015             {
01016                 PrevFocus = Current;
01017             }
01018             
01019             if(Current == DocItem)
01020             {
01021                 Current->u.Link.state |= LIS_FOCUSED;
01022             }
01023             else
01024             {
01025                 Current->u.Link.state &= ~LIS_FOCUSED;
01026             }
01027         }
01028     }
01029     
01030     return PrevFocus;
01031 }
01032 
01033 /***********************************************************************
01034  *           SYSLINK_SetItem
01035  * Sets the states and attributes of a link item.
01036  */
01037 static LRESULT SYSLINK_SetItem (const SYSLINK_INFO *infoPtr, const LITEM *Item)
01038 {
01039     PDOC_ITEM di;
01040     int nc;
01041     PWSTR szId = NULL;
01042     PWSTR szUrl = NULL;
01043     BOOL Repaint = FALSE;
01044 
01045     if(!(Item->mask & LIF_ITEMINDEX) || !(Item->mask & (LIF_FLAGSMASK)))
01046     {
01047         ERR("Invalid Flags!\n");
01048         return FALSE;
01049     }
01050 
01051     di = SYSLINK_GetLinkItemByIndex(infoPtr, Item->iLink);
01052     if(di == NULL)
01053     {
01054         ERR("Link %d couldn't be found\n", Item->iLink);
01055         return FALSE;
01056     }
01057 
01058     if(Item->mask & LIF_ITEMID)
01059     {
01060         nc = min(lstrlenW(Item->szID), MAX_LINKID_TEXT - 1);
01061         szId = Alloc((nc + 1) * sizeof(WCHAR));
01062         if(szId)
01063         {
01064             lstrcpynW(szId, Item->szID, nc + 1);
01065         }
01066         else
01067         {
01068             ERR("Unable to allocate memory for link id\n");
01069             return FALSE;
01070         }
01071     }
01072 
01073     if(Item->mask & LIF_URL)
01074     {
01075         nc = min(lstrlenW(Item->szUrl), L_MAX_URL_LENGTH - 1);
01076         szUrl = Alloc((nc + 1) * sizeof(WCHAR));
01077         if(szUrl)
01078         {
01079             lstrcpynW(szUrl, Item->szUrl, nc + 1);
01080         }
01081         else
01082         {
01083             Free(szId);
01084 
01085             ERR("Unable to allocate memory for link url\n");
01086             return FALSE;
01087         }
01088     }
01089 
01090     if(Item->mask & LIF_ITEMID)
01091     {
01092         Free(di->u.Link.szID);
01093         di->u.Link.szID = szId;
01094     }
01095 
01096     if(Item->mask & LIF_URL)
01097     {
01098         Free(di->u.Link.szUrl);
01099         di->u.Link.szUrl = szUrl;
01100     }
01101 
01102     if(Item->mask & LIF_STATE)
01103     {
01104         UINT oldstate = di->u.Link.state;
01105         /* clear the masked bits */
01106         di->u.Link.state &= ~(Item->stateMask & LIS_MASK);
01107         /* copy the bits */
01108         di->u.Link.state |= (Item->state & Item->stateMask) & LIS_MASK;
01109         Repaint = (oldstate != di->u.Link.state);
01110         
01111         /* update the focus */
01112         SYSLINK_SetFocusLink(infoPtr, ((di->u.Link.state & LIS_FOCUSED) ? di : NULL));
01113     }
01114     
01115     if(Repaint)
01116     {
01117         SYSLINK_RepaintLink(infoPtr, di);
01118     }
01119     
01120     return TRUE;
01121 }
01122 
01123 /***********************************************************************
01124  *           SYSLINK_GetItem
01125  * Retrieves the states and attributes of a link item.
01126  */
01127 static LRESULT SYSLINK_GetItem (const SYSLINK_INFO *infoPtr, PLITEM Item)
01128 {
01129     PDOC_ITEM di;
01130     
01131     if(!(Item->mask & LIF_ITEMINDEX) || !(Item->mask & (LIF_FLAGSMASK)))
01132     {
01133         ERR("Invalid Flags!\n");
01134         return FALSE;
01135     }
01136     
01137     di = SYSLINK_GetLinkItemByIndex(infoPtr, Item->iLink);
01138     if(di == NULL)
01139     {
01140         ERR("Link %d couldn't be found\n", Item->iLink);
01141         return FALSE;
01142     }
01143     
01144     if(Item->mask & LIF_STATE)
01145     {
01146         Item->state = (di->u.Link.state & Item->stateMask);
01147         if(!infoPtr->HasFocus)
01148         {
01149             /* remove the LIS_FOCUSED bit if the control doesn't have focus */
01150             Item->state &= ~LIS_FOCUSED;
01151         }
01152     }
01153     
01154     if(Item->mask & LIF_ITEMID)
01155     {
01156         if(di->u.Link.szID)
01157         {
01158             lstrcpyW(Item->szID, di->u.Link.szID);
01159         }
01160         else
01161         {
01162             Item->szID[0] = 0;
01163         }
01164     }
01165     
01166     if(Item->mask & LIF_URL)
01167     {
01168         if(di->u.Link.szUrl)
01169         {
01170             lstrcpyW(Item->szUrl, di->u.Link.szUrl);
01171         }
01172         else
01173         {
01174             Item->szUrl[0] = 0;
01175         }
01176     }
01177     
01178     return TRUE;
01179 }
01180 
01181 /***********************************************************************
01182  *           SYSLINK_PtInDocItem
01183  * Determines if a point is in the region of a document item
01184  */
01185 static BOOL SYSLINK_PtInDocItem (const DOC_ITEM *DocItem, POINT pt)
01186 {
01187     PDOC_TEXTBLOCK bl;
01188     int n;
01189 
01190     bl = DocItem->Blocks;
01191     if (bl != NULL)
01192     {
01193         n = DocItem->nText;
01194 
01195         while(n > 0)
01196         {
01197             if (PtInRect(&bl->rc, pt))
01198             {
01199                 return TRUE;
01200             }
01201             n -= bl->nChars + bl->nSkip;
01202             bl++;
01203         }
01204     }
01205     
01206     return FALSE;
01207 }
01208 
01209 /***********************************************************************
01210  *           SYSLINK_HitTest
01211  * Determines the link the user clicked on.
01212  */
01213 static LRESULT SYSLINK_HitTest (const SYSLINK_INFO *infoPtr, PLHITTESTINFO HitTest)
01214 {
01215     PDOC_ITEM Current;
01216     int id = 0;
01217 
01218     for(Current = infoPtr->Items; Current != NULL; Current = Current->Next)
01219     {
01220         if(Current->Type == slLink)
01221         {
01222             if(SYSLINK_PtInDocItem(Current, HitTest->pt))
01223             {
01224                 HitTest->item.mask = 0;
01225                 HitTest->item.iLink = id;
01226                 HitTest->item.state = 0;
01227                 HitTest->item.stateMask = 0;
01228                 if(Current->u.Link.szID)
01229                 {
01230                     lstrcpyW(HitTest->item.szID, Current->u.Link.szID);
01231                 }
01232                 else
01233                 {
01234                     HitTest->item.szID[0] = 0;
01235                 }
01236                 if(Current->u.Link.szUrl)
01237                 {
01238                     lstrcpyW(HitTest->item.szUrl, Current->u.Link.szUrl);
01239                 }
01240                 else
01241                 {
01242                     HitTest->item.szUrl[0] = 0;
01243                 }
01244                 return TRUE;
01245             }
01246             id++;
01247         }
01248     }
01249     
01250     return FALSE;
01251 }
01252 
01253 /***********************************************************************
01254  *           SYSLINK_GetIdealHeight
01255  * Returns the preferred height of a link at the current control's width.
01256  */
01257 static LRESULT SYSLINK_GetIdealHeight (const SYSLINK_INFO *infoPtr)
01258 {
01259     HDC hdc = GetDC(infoPtr->Self);
01260     if(hdc != NULL)
01261     {
01262         LRESULT height;
01263         TEXTMETRICW tm;
01264         HGDIOBJ hOldFont = SelectObject(hdc, infoPtr->Font);
01265         
01266         if(GetTextMetricsW(hdc, &tm))
01267         {
01268             height = tm.tmHeight;
01269         }
01270         else
01271         {
01272             height = 0;
01273         }
01274         SelectObject(hdc, hOldFont);
01275         ReleaseDC(infoPtr->Self, hdc);
01276         
01277         return height;
01278     }
01279     return 0;
01280 }
01281 
01282 /***********************************************************************
01283  *           SYSLINK_SendParentNotify
01284  * Sends a WM_NOTIFY message to the parent window.
01285  */
01286 static LRESULT SYSLINK_SendParentNotify (const SYSLINK_INFO *infoPtr, UINT code, const DOC_ITEM *Link, int iLink)
01287 {
01288     NMLINK nml;
01289 
01290     nml.hdr.hwndFrom = infoPtr->Self;
01291     nml.hdr.idFrom = GetWindowLongPtrW(infoPtr->Self, GWLP_ID);
01292     nml.hdr.code = code;
01293 
01294     nml.item.mask = 0;
01295     nml.item.iLink = iLink;
01296     nml.item.state = 0;
01297     nml.item.stateMask = 0;
01298     if(Link->u.Link.szID)
01299     {
01300         lstrcpyW(nml.item.szID, Link->u.Link.szID);
01301     }
01302     else
01303     {
01304         nml.item.szID[0] = 0;
01305     }
01306     if(Link->u.Link.szUrl)
01307     {
01308         lstrcpyW(nml.item.szUrl, Link->u.Link.szUrl);
01309     }
01310     else
01311     {
01312         nml.item.szUrl[0] = 0;
01313     }
01314 
01315     return SendMessageW(infoPtr->Notify, WM_NOTIFY, nml.hdr.idFrom, (LPARAM)&nml);
01316 }
01317 
01318 /***********************************************************************
01319  *           SYSLINK_SetFocus
01320  * Handles receiving the input focus.
01321  */
01322 static LRESULT SYSLINK_SetFocus (SYSLINK_INFO *infoPtr)
01323 {
01324     PDOC_ITEM Focus;
01325     
01326     infoPtr->HasFocus = TRUE;
01327 
01328     /* We always select the first link, even if we activated the control using
01329        SHIFT+TAB. This is the default behavior */
01330     Focus = SYSLINK_GetNextLink(infoPtr, NULL);
01331     if(Focus != NULL)
01332     {
01333         SYSLINK_SetFocusLink(infoPtr, Focus);
01334         SYSLINK_RepaintLink(infoPtr, Focus);
01335     }
01336     return 0;
01337 }
01338 
01339 /***********************************************************************
01340  *           SYSLINK_KillFocus
01341  * Handles losing the input focus.
01342  */
01343 static LRESULT SYSLINK_KillFocus (SYSLINK_INFO *infoPtr)
01344 {
01345     PDOC_ITEM Focus;
01346     
01347     infoPtr->HasFocus = FALSE;
01348     Focus = SYSLINK_GetFocusLink(infoPtr, NULL);
01349     
01350     if(Focus != NULL)
01351     {
01352         SYSLINK_RepaintLink(infoPtr, Focus);
01353     }
01354 
01355     return 0;
01356 }
01357 
01358 /***********************************************************************
01359  *           SYSLINK_LinkAtPt
01360  * Returns a link at the specified position
01361  */
01362 static PDOC_ITEM SYSLINK_LinkAtPt (const SYSLINK_INFO *infoPtr, const POINT *pt, int *LinkId, BOOL MustBeEnabled)
01363 {
01364     PDOC_ITEM Current;
01365     int id = 0;
01366 
01367     for(Current = infoPtr->Items; Current != NULL; Current = Current->Next)
01368     {
01369         if((Current->Type == slLink) && SYSLINK_PtInDocItem(Current, *pt) &&
01370            (!MustBeEnabled || (MustBeEnabled && (Current->u.Link.state & LIS_ENABLED))))
01371         {
01372             if(LinkId != NULL)
01373             {
01374                 *LinkId = id;
01375             }
01376             return Current;
01377         }
01378         id++;
01379     }
01380 
01381     return NULL;
01382 }
01383 
01384 /***********************************************************************
01385  *           SYSLINK_LButtonDown
01386  * Handles mouse clicks
01387  */
01388 static LRESULT SYSLINK_LButtonDown (SYSLINK_INFO *infoPtr, const POINT *pt)
01389 {
01390     PDOC_ITEM Current, Old;
01391     int id;
01392 
01393     Current = SYSLINK_LinkAtPt(infoPtr, pt, &id, TRUE);
01394     if(Current != NULL)
01395     {
01396       SetFocus(infoPtr->Self);
01397 
01398       Old = SYSLINK_SetFocusLink(infoPtr, Current);
01399       if(Old != NULL && Old != Current)
01400       {
01401           SYSLINK_RepaintLink(infoPtr, Old);
01402       }
01403       infoPtr->MouseDownID = id;
01404       SYSLINK_RepaintLink(infoPtr, Current);
01405     }
01406 
01407     return 0;
01408 }
01409 
01410 /***********************************************************************
01411  *           SYSLINK_LButtonUp
01412  * Handles mouse clicks
01413  */
01414 static LRESULT SYSLINK_LButtonUp (SYSLINK_INFO *infoPtr, const POINT *pt)
01415 {
01416     if(infoPtr->MouseDownID > -1)
01417     {
01418         PDOC_ITEM Current;
01419         int id;
01420         
01421         Current = SYSLINK_LinkAtPt(infoPtr, pt, &id, TRUE);
01422         if((Current != NULL) && (Current->u.Link.state & LIS_FOCUSED) && (infoPtr->MouseDownID == id))
01423         {
01424             SYSLINK_SendParentNotify(infoPtr, NM_CLICK, Current, id);
01425         }
01426     }
01427 
01428     infoPtr->MouseDownID = -1;
01429 
01430     return 0;
01431 }
01432 
01433 /***********************************************************************
01434  *           SYSLINK_OnEnter
01435  * Handles ENTER key events
01436  */
01437 static BOOL SYSLINK_OnEnter (const SYSLINK_INFO *infoPtr)
01438 {
01439     if(infoPtr->HasFocus && !infoPtr->IgnoreReturn)
01440     {
01441         PDOC_ITEM Focus;
01442         int id;
01443         
01444         Focus = SYSLINK_GetFocusLink(infoPtr, &id);
01445         if(Focus)
01446         {
01447             SYSLINK_SendParentNotify(infoPtr, NM_RETURN, Focus, id);
01448             return TRUE;
01449         }
01450     }
01451     return FALSE;
01452 }
01453 
01454 /***********************************************************************
01455  *           SYSKEY_SelectNextPrevLink
01456  * Changes the currently focused link
01457  */
01458 static BOOL SYSKEY_SelectNextPrevLink (const SYSLINK_INFO *infoPtr, BOOL Prev)
01459 {
01460     if(infoPtr->HasFocus)
01461     {
01462         PDOC_ITEM Focus;
01463         int id;
01464 
01465         Focus = SYSLINK_GetFocusLink(infoPtr, &id);
01466         if(Focus != NULL)
01467         {
01468             PDOC_ITEM NewFocus, OldFocus;
01469 
01470             if(Prev)
01471                 NewFocus = SYSLINK_GetPrevLink(infoPtr, Focus);
01472             else
01473                 NewFocus = SYSLINK_GetNextLink(infoPtr, Focus);
01474 
01475             if(NewFocus != NULL)
01476             {
01477                 OldFocus = SYSLINK_SetFocusLink(infoPtr, NewFocus);
01478 
01479                 if(OldFocus && OldFocus != NewFocus)
01480                 {
01481                     SYSLINK_RepaintLink(infoPtr, OldFocus);
01482                 }
01483                 SYSLINK_RepaintLink(infoPtr, NewFocus);
01484                 return TRUE;
01485             }
01486         }
01487     }
01488     return FALSE;
01489 }
01490 
01491 /***********************************************************************
01492  *           SYSKEY_SelectNextPrevLink
01493  * Determines if there's a next or previous link to decide whether the control
01494  * should capture the tab key message
01495  */
01496 static BOOL SYSLINK_NoNextLink (const SYSLINK_INFO *infoPtr, BOOL Prev)
01497 {
01498     PDOC_ITEM Focus, NewFocus;
01499 
01500     Focus = SYSLINK_GetFocusLink(infoPtr, NULL);
01501     if(Prev)
01502         NewFocus = SYSLINK_GetPrevLink(infoPtr, Focus);
01503     else
01504         NewFocus = SYSLINK_GetNextLink(infoPtr, Focus);
01505 
01506     return NewFocus == NULL;
01507 }
01508 
01509 /***********************************************************************
01510  *           SYSLINK_GetIdealSize
01511  * Calculates the ideal size of a link control at a given maximum width.
01512  */
01513 static VOID SYSLINK_GetIdealSize (const SYSLINK_INFO *infoPtr, int cxMaxWidth, LPSIZE lpSize)
01514 {
01515     RECT rc;
01516     HDC hdc;
01517 
01518     rc.left = rc.top = rc.bottom = 0;
01519     rc.right = cxMaxWidth;
01520 
01521     hdc = GetDC(infoPtr->Self);
01522     if (hdc != NULL)
01523     {
01524         HGDIOBJ hOldFont = SelectObject(hdc, infoPtr->Font);
01525 
01526         SYSLINK_Render(infoPtr, hdc, &rc);
01527 
01528         SelectObject(hdc, hOldFont);
01529         ReleaseDC(infoPtr->Self, hdc);
01530 
01531         lpSize->cx = rc.right;
01532         lpSize->cy = rc.bottom;
01533     }
01534 }
01535 
01536 /***********************************************************************
01537  *           SysLinkWindowProc
01538  */
01539 static LRESULT WINAPI SysLinkWindowProc(HWND hwnd, UINT message,
01540                                         WPARAM wParam, LPARAM lParam)
01541 {
01542     SYSLINK_INFO *infoPtr;
01543 
01544     TRACE("hwnd=%p msg=%04x wparam=%lx lParam=%lx\n", hwnd, message, wParam, lParam);
01545 
01546     infoPtr = (SYSLINK_INFO *)GetWindowLongPtrW(hwnd, 0);
01547 
01548     if (!infoPtr && message != WM_CREATE)
01549         return DefWindowProcW(hwnd, message, wParam, lParam);
01550 
01551     switch(message) {
01552     case WM_PRINTCLIENT:
01553     case WM_PAINT:
01554         return SYSLINK_Paint (infoPtr, (HDC)wParam);
01555 
01556     case WM_ERASEBKGND:
01557         if (!(infoPtr->Style & LWS_TRANSPARENT))
01558         {
01559             HDC hdc = (HDC)wParam;
01560             HBRUSH brush = CreateSolidBrush( comctl32_color.clrWindow );
01561             RECT rect;
01562 
01563             GetClipBox( hdc, &rect );
01564             FillRect( hdc, &rect, brush );
01565             DeleteObject( brush );
01566             return 1;
01567         }
01568         return 0;
01569 
01570     case WM_SETCURSOR:
01571     {
01572         LHITTESTINFO ht;
01573         DWORD mp = GetMessagePos();
01574         
01575         ht.pt.x = (short)LOWORD(mp);
01576         ht.pt.y = (short)HIWORD(mp);
01577         
01578         ScreenToClient(infoPtr->Self, &ht.pt);
01579         if(SYSLINK_HitTest (infoPtr, &ht))
01580         {
01581             SetCursor(LoadCursorW(0, (LPCWSTR)IDC_HAND));
01582             return TRUE;
01583         }
01584 
01585         return DefWindowProcW(hwnd, message, wParam, lParam);
01586     }
01587 
01588     case WM_SIZE:
01589     {
01590         RECT rcClient;
01591         if (GetClientRect(infoPtr->Self, &rcClient))
01592         {
01593             HDC hdc = GetDC(infoPtr->Self);
01594             if(hdc != NULL)
01595             {
01596                 SYSLINK_Render(infoPtr, hdc, &rcClient);
01597                 ReleaseDC(infoPtr->Self, hdc);
01598             }
01599         }
01600         return 0;
01601     }
01602 
01603     case WM_GETFONT:
01604         return (LRESULT)infoPtr->Font;
01605 
01606     case WM_SETFONT:
01607         return (LRESULT)SYSLINK_SetFont(infoPtr, (HFONT)wParam, (BOOL)lParam);
01608 
01609     case WM_SETTEXT:
01610         SYSLINK_SetText(infoPtr, (LPWSTR)lParam);
01611         return DefWindowProcW(hwnd, message, wParam, lParam);
01612 
01613     case WM_LBUTTONDOWN:
01614     {
01615         POINT pt;
01616         pt.x = (short)LOWORD(lParam);
01617         pt.y = (short)HIWORD(lParam);
01618         return SYSLINK_LButtonDown(infoPtr, &pt);
01619     }
01620     case WM_LBUTTONUP:
01621     {
01622         POINT pt;
01623         pt.x = (short)LOWORD(lParam);
01624         pt.y = (short)HIWORD(lParam);
01625         return SYSLINK_LButtonUp(infoPtr, &pt);
01626     }
01627     
01628     case WM_KEYDOWN:
01629     {
01630         switch(wParam)
01631         {
01632         case VK_RETURN:
01633             SYSLINK_OnEnter(infoPtr);
01634             return 0;
01635         case VK_TAB:
01636         {
01637             BOOL shift = GetKeyState(VK_SHIFT) & 0x8000;
01638             SYSKEY_SelectNextPrevLink(infoPtr, shift);
01639             return 0;
01640         }
01641         default:
01642             return DefWindowProcW(hwnd, message, wParam, lParam);
01643         }
01644     }
01645     
01646     case WM_GETDLGCODE:
01647     {
01648         LRESULT Ret = DLGC_HASSETSEL;
01649         int vk = (lParam != 0 ? (int)((LPMSG)lParam)->wParam : 0);
01650         switch(vk)
01651         {
01652         case VK_RETURN:
01653             Ret |= DLGC_WANTMESSAGE;
01654             break;
01655         case VK_TAB:
01656         {
01657             BOOL shift = GetKeyState(VK_SHIFT) & 0x8000;
01658             if(!SYSLINK_NoNextLink(infoPtr, shift))
01659             {
01660                 Ret |= DLGC_WANTTAB;
01661             }
01662             else
01663             {
01664                 Ret |= DLGC_WANTCHARS;
01665             }
01666             break;
01667         }
01668         }
01669         return Ret;
01670     }
01671     
01672     case WM_NCHITTEST:
01673     {
01674         POINT pt;
01675         RECT rc;
01676         pt.x = (short)LOWORD(lParam);
01677         pt.y = (short)HIWORD(lParam);
01678         
01679         GetClientRect(infoPtr->Self, &rc);
01680         ScreenToClient(infoPtr->Self, &pt);
01681         if(pt.x < 0 || pt.y < 0 || pt.x > rc.right || pt.y > rc.bottom)
01682         {
01683             return HTNOWHERE;
01684         }
01685 
01686         if(SYSLINK_LinkAtPt(infoPtr, &pt, NULL, FALSE))
01687         {
01688             return HTCLIENT;
01689         }
01690         
01691         return HTTRANSPARENT;
01692     }
01693 
01694     case LM_HITTEST:
01695         return SYSLINK_HitTest(infoPtr, (PLHITTESTINFO)lParam);
01696 
01697     case LM_SETITEM:
01698         return SYSLINK_SetItem(infoPtr, (PLITEM)lParam);
01699 
01700     case LM_GETITEM:
01701         return SYSLINK_GetItem(infoPtr, (PLITEM)lParam);
01702 
01703     case LM_GETIDEALHEIGHT:
01704         if (lParam)
01705         {
01706             /* LM_GETIDEALSIZE */
01707             SYSLINK_GetIdealSize(infoPtr, (int)wParam, (LPSIZE)lParam);
01708         }
01709         return SYSLINK_GetIdealHeight(infoPtr);
01710 
01711     case WM_SETFOCUS:
01712         return SYSLINK_SetFocus(infoPtr);
01713 
01714     case WM_KILLFOCUS:
01715         return SYSLINK_KillFocus(infoPtr);
01716 
01717     case WM_ENABLE:
01718         infoPtr->Style &= ~WS_DISABLED;
01719         infoPtr->Style |= (wParam ? 0 : WS_DISABLED);
01720         InvalidateRect (infoPtr->Self, NULL, FALSE);
01721         return 0;
01722 
01723     case WM_STYLECHANGED:
01724         if (wParam == GWL_STYLE)
01725         {
01726             infoPtr->Style = ((LPSTYLESTRUCT)lParam)->styleNew;
01727 
01728             InvalidateRect(infoPtr->Self, NULL, TRUE);
01729         }
01730         return 0;
01731 
01732     case WM_CREATE:
01733         /* allocate memory for info struct */
01734         infoPtr = Alloc (sizeof(SYSLINK_INFO));
01735         if (!infoPtr) return -1;
01736         SetWindowLongPtrW (hwnd, 0, (DWORD_PTR)infoPtr);
01737 
01738         /* initialize the info struct */
01739         infoPtr->Self = hwnd;
01740         infoPtr->Notify = ((LPCREATESTRUCTW)lParam)->hwndParent;
01741         infoPtr->Style = ((LPCREATESTRUCTW)lParam)->style;
01742         infoPtr->Font = 0;
01743         infoPtr->LinkFont = 0;
01744         infoPtr->Items = NULL;
01745         infoPtr->HasFocus = FALSE;
01746         infoPtr->MouseDownID = -1;
01747         infoPtr->TextColor = comctl32_color.clrWindowText;
01748         infoPtr->LinkColor = comctl32_color.clrHighlight;
01749         infoPtr->VisitedColor = comctl32_color.clrHighlight;
01750         infoPtr->BreakChar = ' ';
01751         infoPtr->IgnoreReturn = infoPtr->Style & LWS_IGNORERETURN;
01752         TRACE("SysLink Ctrl creation, hwnd=%p\n", hwnd);
01753         SYSLINK_SetText(infoPtr, ((LPCREATESTRUCTW)lParam)->lpszName);
01754         return 0;
01755 
01756     case WM_DESTROY:
01757         TRACE("SysLink Ctrl destruction, hwnd=%p\n", hwnd);
01758         SYSLINK_ClearDoc(infoPtr);
01759         if(infoPtr->Font != 0) DeleteObject(infoPtr->Font);
01760         if(infoPtr->LinkFont != 0) DeleteObject(infoPtr->LinkFont);
01761         SetWindowLongPtrW(hwnd, 0, 0);
01762         Free (infoPtr);
01763         return 0;
01764 
01765     case WM_SYSCOLORCHANGE:
01766         COMCTL32_RefreshSysColors();
01767         return 0;
01768 
01769     default:
01770         if ((message >= WM_USER) && (message < WM_APP) && !COMCTL32_IsReflectedMessage(message))
01771         {
01772             ERR("unknown msg %04x wp=%04lx lp=%08lx\n", message, wParam, lParam );
01773         }
01774         return DefWindowProcW(hwnd, message, wParam, lParam);
01775     }
01776 }
01777 
01778 
01779 /***********************************************************************
01780  * SYSLINK_Register [Internal]
01781  *
01782  * Registers the SysLink window class.
01783  */
01784 VOID SYSLINK_Register (void)
01785 {
01786     WNDCLASSW wndClass;
01787 
01788     ZeroMemory (&wndClass, sizeof(wndClass));
01789     wndClass.style         = CS_GLOBALCLASS | CS_VREDRAW | CS_HREDRAW;
01790     wndClass.lpfnWndProc   = SysLinkWindowProc;
01791     wndClass.cbClsExtra    = 0;
01792     wndClass.cbWndExtra    = sizeof (SYSLINK_INFO *);
01793     wndClass.hCursor       = LoadCursorW (0, (LPWSTR)IDC_ARROW);
01794     wndClass.lpszClassName = WC_LINK;
01795 
01796     RegisterClassW (&wndClass);
01797 }
01798 
01799 
01800 /***********************************************************************
01801  * SYSLINK_Unregister [Internal]
01802  *
01803  * Unregisters the SysLink window class.
01804  */
01805 VOID SYSLINK_Unregister (void)
01806 {
01807     UnregisterClassW (WC_LINK, NULL);
01808 }

Generated on Sat May 26 2012 04:21:38 for ReactOS by doxygen 1.7.6.1

ReactOS is a registered trademark or a trademark of ReactOS Foundation in the United States and other countries.