📄 syslink.c
字号:
/*
* SysLink control
*
* Copyright 2004 - 2006 Thomas Weidenmueller <w3seek@reactos.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*
* NOTES
*
* This code was audited for completeness against the documented features
* of Comctl32.dll version 6.0 on Apr. 4, 2005, by Dimitrie O. Paun.
*
* Unless otherwise noted, we believe this code to be complete, as per
* the specification mentioned above.
* If you discover missing features, or bugs, please note them below.
*/
#include <stdarg.h>
#include <string.h>
#include "windef.h"
#include "winbase.h"
#include "wingdi.h"
#include "winuser.h"
#include "winnls.h"
#include "commctrl.h"
#include "comctl32.h"
#include "wine/unicode.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(progress);
INT WINAPI StrCmpNIW(LPCWSTR,LPCWSTR,INT);
typedef struct
{
int nChars;
int nSkip;
RECT rc;
} DOC_TEXTBLOCK, *PDOC_TEXTBLOCK;
#define LIF_FLAGSMASK (LIF_STATE | LIF_ITEMID | LIF_URL)
#define LIS_MASK (LIS_FOCUSED | LIS_ENABLED | LIS_VISITED)
typedef enum
{
slText = 0,
slLink
} SL_ITEM_TYPE;
typedef struct _DOC_ITEM
{
struct _DOC_ITEM *Next; /* Address to the next item */
UINT nText; /* Number of characters of the text */
SL_ITEM_TYPE Type; /* type of the item */
PDOC_TEXTBLOCK Blocks; /* Array of text blocks */
union
{
struct
{
UINT state; /* Link state */
WCHAR *szID; /* Link ID string */
WCHAR *szUrl; /* Link URL string */
} Link;
struct
{
UINT Dummy;
} Text;
} u;
WCHAR Text[1]; /* Text of the document item */
} DOC_ITEM, *PDOC_ITEM;
typedef struct
{
HWND Self; /* The window handle for this control */
HWND Notify; /* The parent handle to receive notifications */
DWORD Style; /* Styles for this control */
PDOC_ITEM Items; /* Address to the first document item */
BOOL HasFocus; /* Whether the control has the input focus */
int MouseDownID; /* ID of the link that the mouse button first selected */
HFONT Font; /* Handle to the font for text */
HFONT LinkFont; /* Handle to the font for links */
COLORREF TextColor; /* Color of the text */
COLORREF LinkColor; /* Color of links */
COLORREF VisitedColor; /* Color of visited links */
WCHAR BreakChar; /* Break Character for the current font */
} SYSLINK_INFO;
static const WCHAR SL_LINKOPEN[] = { '<','a', 0 };
static const WCHAR SL_HREF[] = { 'h','r','e','f','=','\"',0 };
static const WCHAR SL_ID[] = { 'i','d','=','\"',0 };
static const WCHAR SL_LINKCLOSE[] = { '<','/','a','>',0 };
/* Control configuration constants */
#define SL_LEFTMARGIN (0)
#define SL_TOPMARGIN (0)
#define SL_RIGHTMARGIN (0)
#define SL_BOTTOMMARGIN (0)
/***********************************************************************
* SYSLINK_FreeDocItem
* Frees all data and gdi objects associated with a document item
*/
static VOID SYSLINK_FreeDocItem (PDOC_ITEM DocItem)
{
if(DocItem->Type == slLink)
{
if (DocItem->u.Link.szID != NULL)
{
Free(DocItem->u.Link.szID);
}
if (DocItem->u.Link.szUrl != NULL)
{
Free(DocItem->u.Link.szUrl);
}
}
/* we don't free Text because it's just a pointer to a character in the
entire window text string */
Free(DocItem);
}
/***********************************************************************
* SYSLINK_AppendDocItem
* Create and append a new document item.
*/
static PDOC_ITEM SYSLINK_AppendDocItem (SYSLINK_INFO *infoPtr, LPCWSTR Text, UINT textlen,
SL_ITEM_TYPE type, PDOC_ITEM LastItem)
{
PDOC_ITEM Item;
textlen = min(textlen, lstrlenW(Text));
Item = Alloc(FIELD_OFFSET(DOC_ITEM, Text[textlen + 1]));
if(Item == NULL)
{
ERR("Failed to alloc DOC_ITEM structure!\n");
return NULL;
}
Item->Next = NULL;
Item->nText = textlen;
Item->Type = type;
Item->Blocks = NULL;
if(LastItem != NULL)
{
LastItem->Next = Item;
}
else
{
infoPtr->Items = Item;
}
lstrcpynW(Item->Text, Text, textlen + 1);
return Item;
}
/***********************************************************************
* SYSLINK_ClearDoc
* Clears the document tree
*/
static VOID SYSLINK_ClearDoc (SYSLINK_INFO *infoPtr)
{
PDOC_ITEM Item, Next;
Item = infoPtr->Items;
while(Item != NULL)
{
Next = Item->Next;
SYSLINK_FreeDocItem(Item);
Item = Next;
}
infoPtr->Items = NULL;
}
/***********************************************************************
* SYSLINK_ParseText
* Parses the window text string and creates a document. Returns the
* number of document items created.
*/
static UINT SYSLINK_ParseText (SYSLINK_INFO *infoPtr, LPCWSTR Text)
{
LPCWSTR current, textstart = NULL, linktext = NULL, firsttag = NULL;
int taglen = 0, textlen = 0, linklen = 0, docitems = 0;
PDOC_ITEM Last = NULL;
SL_ITEM_TYPE CurrentType = slText;
LPCWSTR lpID, lpUrl;
UINT lenId, lenUrl;
for(current = Text; *current != 0;)
{
if(*current == '<')
{
if(!StrCmpNIW(current, SL_LINKOPEN, 2) && (CurrentType == slText))
{
BOOL ValidParam = FALSE, ValidLink = FALSE;
if(*(current + 2) == '>')
{
/* we just have to deal with a <a> tag */
taglen = 3;
ValidLink = TRUE;
ValidParam = TRUE;
firsttag = current;
linklen = 0;
lpID = NULL;
lpUrl = NULL;
}
else if(*(current + 2) == infoPtr->BreakChar)
{
/* we expect parameters, parse them */
LPCWSTR *CurrentParameter = NULL, tmp;
UINT *CurrentParameterLen = NULL;
taglen = 3;
tmp = current + taglen;
lpID = NULL;
lpUrl = NULL;
CheckParameter:
/* compare the current position with all known parameters */
if(!StrCmpNIW(tmp, SL_HREF, 6))
{
taglen += 6;
ValidParam = TRUE;
CurrentParameter = &lpUrl;
CurrentParameterLen = &lenUrl;
}
else if(!StrCmpNIW(tmp, SL_ID, 4))
{
taglen += 4;
ValidParam = TRUE;
CurrentParameter = &lpID;
CurrentParameterLen = &lenId;
}
else
{
ValidParam = FALSE;
}
if(ValidParam)
{
/* we got a known parameter, now search until the next " character.
If we can't find a " character, there's a syntax error and we just assume it's text */
ValidParam = FALSE;
*CurrentParameter = current + taglen;
*CurrentParameterLen = 0;
for(tmp = *CurrentParameter; *tmp != 0; tmp++)
{
taglen++;
if(*tmp == '\"')
{
ValidParam = TRUE;
tmp++;
break;
}
(*CurrentParameterLen)++;
}
}
if(ValidParam)
{
/* we're done with this parameter, now there are only 2 possibilities:
* 1. another parameter is coming, so expect a ' ' (space) character
* 2. the tag is being closed, so expect a '<' character
*/
if(*tmp == infoPtr->BreakChar)
{
/* we expect another parameter, do the whole thing again */
taglen++;
tmp++;
goto CheckParameter;
}
else if(*tmp == '>')
{
/* the tag is being closed, we're done */
ValidLink = TRUE;
taglen++;
}
else
tmp++;
}
}
if(ValidLink && ValidParam)
{
/* the <a ...> tag appears to be valid. save all information
so we can add the link if we find a valid </a> tag later */
CurrentType = slLink;
linktext = current + taglen;
linklen = 0;
firsttag = current;
}
else
{
taglen = 1;
lpID = NULL;
lpUrl = NULL;
if(textstart == NULL)
{
textstart = current;
}
}
}
else if(!StrCmpNIW(current, SL_LINKCLOSE, 4) && (CurrentType == slLink) && firsttag)
{
/* there's a <a...> tag opened, first add the previous text, if present */
if(textstart != NULL && textlen > 0 && firsttag > textstart)
{
Last = SYSLINK_AppendDocItem(infoPtr, textstart, firsttag - textstart, slText, Last);
if(Last == NULL)
{
ERR("Unable to create new document item!\n");
return docitems;
}
docitems++;
textstart = NULL;
textlen = 0;
}
/* now it's time to add the link to the document */
current += 4;
if(linktext != NULL && linklen > 0)
{
Last = SYSLINK_AppendDocItem(infoPtr, linktext, linklen, slLink, Last);
if(Last == NULL)
{
ERR("Unable to create new document item!\n");
return docitems;
}
docitems++;
if(CurrentType == slLink)
{
int nc;
if(!(infoPtr->Style & WS_DISABLED))
{
Last->u.Link.state |= LIS_ENABLED;
}
/* Copy the tag parameters */
if(lpID != NULL)
{
nc = min(lenId, strlenW(lpID));
nc = min(nc, MAX_LINKID_TEXT - 1);
Last->u.Link.szID = Alloc((nc + 1) * sizeof(WCHAR));
if(Last->u.Link.szID != NULL)
{
lstrcpynW(Last->u.Link.szID, lpID, nc + 1);
}
}
else
Last->u.Link.szID = NULL;
if(lpUrl != NULL)
{
nc = min(lenUrl, strlenW(lpUrl));
nc = min(nc, L_MAX_URL_LENGTH - 1);
Last->u.Link.szUrl = Alloc((nc + 1) * sizeof(WCHAR));
if(Last->u.Link.szUrl != NULL)
{
lstrcpynW(Last->u.Link.szUrl, lpUrl, nc + 1);
}
}
else
Last->u.Link.szUrl = NULL;
}
linktext = NULL;
}
CurrentType = slText;
firsttag = NULL;
textstart = NULL;
continue;
}
else
{
/* we don't know what tag it is, so just continue */
taglen = 1;
linklen++;
if(CurrentType == slText && textstart == NULL)
{
textstart = current;
}
}
textlen += taglen;
current += taglen;
}
else
{
textlen++;
linklen++;
/* save the pointer of the current text item if we couldn't find a tag */
if(textstart == NULL && CurrentType == slText)
{
textstart = current;
}
current++;
}
}
if(textstart != NULL && textlen > 0)
{
Last = SYSLINK_AppendDocItem(infoPtr, textstart, textlen, CurrentType, Last);
if(Last == NULL)
{
ERR("Unable to create new document item!\n");
return docitems;
}
if(CurrentType == slLink)
{
int nc;
if(!(infoPtr->Style & WS_DISABLED))
{
Last->u.Link.state |= LIS_ENABLED;
}
/* Copy the tag parameters */
if(lpID != NULL)
{
nc = min(lenId, strlenW(lpID));
nc = min(nc, MAX_LINKID_TEXT - 1);
Last->u.Link.szID = Alloc((nc + 1) * sizeof(WCHAR));
if(Last->u.Link.szID != NULL)
{
lstrcpynW(Last->u.Link.szID, lpID, nc + 1);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -