Hyperlink class (3)

This class provides a window which supports an image, or a piece of text, or both, and which can be clicked. The window provides feedback as you move the mouse over it. Clicking the window acts like double-clicking a file in Windows Explorer, in that it will open the specified document or folder, or run the specified program, or launch a browser and go to the specified URL, or launch a mail client to send an e-mail to the specified address. It is intended for use mostly as a link to an URL or e-mail address, but can be used for other purposes such as launching a ReadMe file. Although this is its main purpose, it is possible to derive a new class from CHyperlink and override the OnClick() method so that you can perform your own action when the left mouse button is clicked. A similar override (OnRClick() ) is provided for clicking the right mouse button, although the default does nothing. You might want to display a popup menu when the user right-clicks.

There are other hyperlink classes on CodeGuru at the moment, but the others don't seem to support changing the image at run time; mine is slightly more configurable, and is not limited to dialogue boxes (it will work on form views and other windows, with some restrictions on window colour mentioned in the documentation) or URLs (firing up a ReadMe.TXT on the hard drive, for example). Mine makes more effort to allow the appearance to be changed, i.e., text alignment and positioning; the fact that it can support text AND / or an image rather than one or the other only; the picture can be loaded transparently; easier font manipulation, etc. It also supports the ability to derive from it and override the behaviour when you left or right click - so you can produce a pop-up menu, for example, or have it open a different window (in your app, not necessarily a browser window) instead of the default 'go to URL' behaviour.

The image can be from a resource in your project, or from a file. It can be loaded transparently, if desired - the first pixel will be taken as the background colour to make transparent. Note, though, that for this to work your parent window must not change the background brush or do any painting over the background which changes its colour from the default (as chosen in the display properties 'Appearance' tab). Only bitmaps are supported in this revision of the class. You do not have to have an image - by default it has none. Note also, though, that the larger the image gets, the more it will flicker as you move the mouse on or off of it - keep it fairly small.

The accompanying text, if any, can be aligned to the left, right or centre; it can be positioned above, below or superimposed over the image at the top, middle or bottom. It can be wrapped (the default), or you can force it not to wrap. Its font can be user-selected, or you can apply simple discrete changes such as size, font name, effects (only bold or italic supported), or both. Note, though, that for this to work you must use a TrueType font and you must set the AllowModsToFont flag. If you have a font which is not TrueType (for example, the default if you select no font of your own is System), the tweaks to the font will have no effect even if the flag is set, since the font can't support those changes. You do not have to have text at all. Note, though, that if you attempt to have no image and no text either, it will be forced to have text - if you have set the text to an empty string, it will default to 'Click here'.

The highlight can be the image only (it gains a border in the active text colour), the text only (it changes to the active text colour and gains an underline), or both. The colours for the inactive text, active text and (text) background can be selected, including transparent for the background.

The target of the link does not need to be explicitly set (by default, there is none). If the text of the hyperlink (whether text is being displayed or not) represents a valid URL / document / executable / e-mail address (basically, anything which can be launched from Windows Explorer), that text will be used as the link target. For an e-mail address (anything with '@' in it), 'mailto:' will be added to the front if necessary. You can, of course, set any link you want. If the link cannot be activated (no program associated with the file type, for example), clicking the link will have no effect.

All of these parameters can be changed once the window is showing; the change will be reflected immediately. You can find out how big the smallest window needed to show the full hyperlink with the current settings must be by calling CalculateRequiredSize() with a CRect object - but the window MUST have already been created when you call this, otherwise you will get an assertion (it does not have to be showing, though). This allows you to find out how much space is needed so that you can plan around it.

 

Supported methods:

void CalculateRequiredSize(CRect& rctHyperlink)
Call this once the settings have been entered and the hyperlink object has been created, but before it is shown, to determine how much space it requires to show fully. If you supply more space than the window requires, the hyperlink area will be centred. If you do not supply enough space, it will still be centred but will be clipped outside the window. Note that no matter how big the window you create is, the link will only appear active if the mouse is moved over the image or the text. For the text, moving anywhere within the rectangle which fully encloses the text will cause the link to appear active - if this rectangle is narrower than the image, then the whole width of the image is part of the active area - I might change this behaviour if people feel it should only be as wide as the text. Note, though, that if the text wraps and some lines are smaller than others, the active rectangle is ALWAYS at least the width of the longest line - this would be near impossible to change.

void OnClick(void) (override)
Called when the left mouse button is clicked over the image or text. The default action is to activate the link specified by the object.

void OnRClick(void) (override)
Called when the right mouse button is clicked over the image or text. There is no default action.

Note: The window uses the standard CWnd Create() method. This is designed so that you can change the window class if desired, although pay attention to the information given in SetTransparentImage() regarding the colour of the window background.

The text which appears in the hyperlink is the standard window caption, so either specify a caption in the Create() call or call SetWindowText() on the object.

 

Image Functions

void GetBitmapRect(CRect& rctBitmap)
Use this to retrieve the rectangle occupied by the bitmap. Note that if the text is superimposed over the top of the image, this rectangle will overlap that returned by GetTextRect().

void SetBitmapFilename(CString strFilename)
Sets the full path and filename of the bitmap to use. It can be an empty string to remove any image from the hyperlink. This method and SetBitmapID() are mutually exclusive - calling this method also removes any bitmap ID currently stored. Default is an empty string.

void SetBitmapID(UINT uID)
Sets the project resource ID of the bitmap to use. It can be 0 to remove any image from the hyperlink. This method and SetBitmapFilename() are mutually exclusive - calling this method also removes any bitmap filename currently stored. Default is 0.

void SetNoBitmap(void)
An alternative method to remove any image from the hyperlink. This removes any bitmap resource ID or filename currently stored.

void SetTransparentBitmap(BOOL bTransparent = TRUE)
Call this to indicate whether or not the bitmap should be displayed transparently. This is achieved by taking the colour of the first pixel of the bitmap and replacing all further occurrences of that colour with the window background colour. For CDialog-derived or CFormView-derived windows, this is the button face colour as chosen in the display properties 'Appearance' tab. For any other window base class, this is the normal window background colour chosen in Appearance. CPropertyPage is CDialog-derived, and so will correctly display the background - if you want to put one of these windows on the CPropertySheet itself, you will not be able to use transparency since CPropertySheet is not derived from CDialog. Other special window classes may have the same problem - always check the class hierarchy to determine if transparency will work. Default is FALSE.

 

Text Functions

void GetTextRect(CRect& rctText)
Use this to retrieve the rectangle occupied by the text. Note that if the text is superimposed over the top of the image, this rectangle will overlap that returned by GetBitmapRect().

COLORREF GetBackColour(void)
COLORREF GetTextColour(void)
COLORREF GetActiveTextColour(void)

These are used to retrieve the colours used for the background of the text area, for the inactive text, and for the active text (mouse is over the hyperlink). Note that the background colour can be HL_BACKCOLOUR_TRANSPARENT. Note also that if the text is being superimposed over the image (position is top, middle or bottom), then the background of the text is transparent regardless of the background colour setting - but the actual setting will be returned anyway. See below for defaults.

void SetBackColour(COLORREF dwColour)
void SetTextColour(COLORREF dwColour)
void SetActiveTextColour(COLORREF dwColour)

These are used to change the colours used for the background of the text area, for the inactive text, and for the active text (mouse is over the hyperlink). The background colour can be HL_BACKCOLOUR_TRANSPARENT to leave the pixels underneath blank areas unaltered. Note that if the text is being superimposed over the image (position is top, middle or bottom), then the background of the text is transparent regardless of the background colour setting - but the actual setting is retained anyway. Note also that the border used to highlight the image is always the same as the active text colour. Default background colour is transparent. Default inactive text colour is black. Default active text colour is bright green.

void SetTextPosition(UINT uPosition, UINT uAlignment)
Indicates where the text should appear, and how. Position can be HL_TEXTPOS_NONE (no text showing), HL_TEXTPOS_ABOVE or HL_TEXTPOS_BELOW for separate text, or HL_TEXTPOS_TOP, HL_TEXTPOS_MIDDLE or HL_TEXTPOS_BOTTOM for text superimposed over the image. Alignment can be HL_TEXTALIGN_LEFT, HL_TEXTALIGN_CENTRE or HL_TEXTALIGN_RIGHT. Default position is below. Default alignment is centred.

Note that if the text is narrower than the image, the text will be aligned relative to the image as expected; but if the text is wider than the image, the image will be aligned relative to the text - therefore, the text will appear centred in the hyperlink window with the image's right-hand edge aligned with the text's right-hand edge (for HL_TEXTALIGN_RIGHT). Similar rules apply if the text is taller than the image. The rectangle returned by CalculateRequiredSize() is ALWAYS the smallest window which fully encloses the text and image, with these rules obeyed, and will not always use the image as the centrepiece - therefore, care must be taken when designing the image and text for this window - the demo program should be able to give you a good idea of how this works.

void SetTextWrapping(BOOL bWrap = TRUE)
Indicates whether text is allowed to wrap or not. Carriage returns and line feeds can be embedded in the string to force new lines, but only if wrapping is enabled - they will show as undefined characters if wrapping is disabled. It is not advisable to prevent wrapping, since it could well result in the text being clipped outside the window. Default is TRUE.

void SetFontToUse(CFont *pFont, BOOL bAllowMods = FALSE)
Allows a custom font to be used for the text. Parameter can be NULL to revert back to the default window font. The second parameter says whether the font (either the one passed in or the default being used) can be modified by calls to the font manipulation routines. Default font is NULL (uses default window font). Default for modifying the font is FALSE.

void AllowModsToFont(BOOL bAllowMods = TRUE)
Indicates whether or not the default window font or user-supplied font can be modified using the discrete font methods. You do NOT have to call this before you call the other methods, but you will have to call it before the hyperlink is displayed for the other font settings to have any effect. You can, of course, call it while the hyperlink is showing, in which case the font changes (if any) will be reflected immediately. Note that if the font being used is not TrueType and can't handle changes in attributes, this method will have no effect. Default is FALSE.

void SetFontName(CString strName)
Specifies the typeface name of the current font, and should be something like "Arial", "Comic Sans MS", "Times New Roman", etc. It can be an empty string to leave the font typeface as the default window font or user-supplied font specifies. Only has an effect if the AllowModsToFont flag is set. If that is set, it can modify the font whether it is the default window font or your user-supplied font. Default is no name (no mods).

void SetFontSize(UINT uHeight, UINT uWidth = 0)
Changes the size of the current font. Width can be 0 to let the system choose the best width in proportion to the height. Height can be 0 to leave the font size as the default window font or user-supplied font specifies. Only has an effect if the AllowModsToFont flag is set. If that is set, it can modify the font whether it is the default window font or your user-supplied font. Default is 0 for both parameters (no mods).

void SetFontAttributes(BOOL bBold, BOOL bItalic)
Changes the style of the current font. The constants HL_FONTBOLD, HL_FONTNOTBOLD, HL_FONTITALIC and HL_FONTNOTITALIC can be used, or just plain TRUE or FALSE. Only has an effect if the AllowModsToFont flag is set. If that is set, it can modify the font whether it is the default window font or your user-supplied font. Default is FALSE for both parameters. Notice that there is no way to leave the parameters alone with this call - if you have made a previous call to this method which changes these attributes, you will have to call this method again to undo that change. It is not much of a problem. Note also that you cannot control the underline - it is built in.

 

Miscellaneous Functions

void SetHighlightStyle(UINT uStyle)
Changes the feedback given when the mouse moves over the hyperlink. It can be HL_HIGHLIGHT_TEXTONLY, HL_HIGHLIGHT_IMAGEONLY or HL_HIGHLIGHT_TEXTANDIMAGE. The colour of the text will change regardless of these settings, but you can set the active text colour to the same as the inactive text colour to stop the highlight of the text - the image will still get a border, though, unless you specify highlighting the text only. Default is to highlight both.

void SetLinkTarget(CString strTarget)
Sets the target to use when the link is clicked. This can be a folder name, such as "C:\\TEMP", to display the folder's contents; it can be any file whose file type has an associated program within Windows Explorer, such as "C:\\IMAGES\\STORM.GIF", to display or act upon that file; it can be an executable or batch file, such as "C:\\TEMP\\TESTPROG.EXE", to run that file; it can be an URL, such as "http://www.codeguru.com", to launch the default browser and go to that URL; or it can be an e-mail address, such as "jteagle@geocities.com", to launch a mail client to start a message to that address. Note that the e-mail address does not need to include 'mailto:' to work - the hyperlink code will add that if necessary. The link does not need to be explicitly set; it will use the hyperlink's text (whether text is being displayed or not) as the link, and therefore you can just set the caption to a valid value. Default is an empty string. Long pathnames and filenames are fully supported.

HCURSOR SetActiveCursor(HCURSOR hCursor)
If you don't like the small hand cursor which currently appears when the mouse is moved over the hyperlink, you can use this to set your own cursor. The cursor must be appropriate for the user's display type, otherwise it won't show - be careful. Although the internal hand cursor is automatically destroyed when the hyperlink object is destroyed, you are responsible for destroying your own cursors, even if you set them into the hyperlink object using this method - it will not delete user cursors. The value returned is the previous user cursor, or NULL if this is the first time you have set a user cursor since this hyperlink object was created.

 

Using CHyperlink

Use it just like any other child control. An example of how to call Create() from within a CDialog or CFormView (use OnInitialUpdate() in place of OnInitDialog() ) is:


BOOL CMyDlg::OnInitDialog(void)
{
    CRect   rctWindow(0, 0, 100, 100);

    m_wndHyperlink.Create(NULL,"Click here to read\r\nthe ReadMe",
            WS_CHILD, rctWindow, this, 9999);

    m_wndHyperlink.SetBitmapID(ID_BMP_DOCUMENTSYMBOL);
    m_wndHyperlink.SetLinkTarget("ReadMe.TXT");
    m_wndHyperlink.SetTextColour(0x000000FF); // Bright red.
    m_wndHyperlink.SetActiveTextColour(0x00FF00FF); // Bright magenta.
    m_wndHyperlink.AllowModsToFont();
    m_wndHyperlink.SetFontName("Times New Roman");
    m_wndHyperlink.SetFontSize(18); // Width is best-fit.
    m_wndHyperlink.SetFontAttributes(HL_FONTNOTBOLD, HL_FONTITALIC);
    m_wndHyperlink.SetTextPosition(HL_TEXTPOS_ABOVE, HL_TEXTALIGN_CENTRE);

    m_wndHyperlink.ShowWindow(SW_SHOWNORMAL);
}

Downloads

Demo project - 33Kb
Source code - 7Kb

The demo demonstrates most of the features of the class, but not quite all. It should be enough to get you going. It relies on Comic Sans MS font being installed on your machine, although it will choose a suitable substitute from your machine as necessary.



Comments

  • Minor compiler warnings at warning level 4 of VC 6.0

    Posted by Legacy on 04/14/2001 12:00am

    Originally posted by: Brett Bock

    There are 7 complier warnings when compiling at level 4.  They are easily fixed by changing Hyperlink.cpp:
    
    

    line 327,
    LRESULT CHyperlink::OnSetText(WPARAM /*wParam*/, LPARAM /*lParam*/)

    lines 457, 458,
    HINSTANCE hInst = NULL ;
    LPCTSTR lpszName = NULL ;

    and initializing the variables on line 666.

    Reply
  • a little improvement

    Posted by Legacy on 03/20/2001 12:00am

    Originally posted by: jerry

    1. when there is no bitmap, the text alignment can't changed from HL_TEXTALIGN_CENTRE to HL_TEXTALIGN_LEFT or HL_TEXTALIGN_RIGHT. I enable this by modifying the function
    CHyperlink::AdjustPositions.

    origin:
    void CHyperlink::AdjustPositions(CRect& rctHyperlink)
    {
    ...
    // Adjust the rects for the true window size.
    GetClientRect(&rctClient);
    iLeft = (rctClient.Width() - rctHyperlink.Width() ) / 2 ;
    iTop = (rctClient.Height() - rctHyperlink.Height() ) / 2 ;
    m_rctText.OffsetRect(iLeft, iTop);
    m_rctImage.OffsetRect(iLeft, iTop);

    if (pBmp != NULL)
    {
    pBmp->DeleteObject();
    delete pBmp ;
    }
    }

    new:
    if (pBmp != NULL)
    {
    GetClientRect(&rctClient);
    iLeft = (rctClient.Width() - rctHyperlink.Width() ) / 2 ;
    iTop = (rctClient.Height() - rctHyperlink.Height() ) / 2 ;
    m_rctText.OffsetRect(iLeft, iTop);
    m_rctImage.OffsetRect(iLeft, iTop);

    pBmp->DeleteObject();
    delete pBmp ;
    }
    }

    2. When I subclass a dialog control using CHyperlink, the hand cursor not displayed when the control actived. I move the code of creating the hand cursor from OnCreate to the constractor.

    3.Carriage returns and linefeeds cann't break the line without TextWrapping attribute. After replacing

    if (!m_bWrapText)
    uFlags |= DT_SINGLELINE ;
    else
    uFlags |= DT_WORDBREAK ;
    with
    if (m_bWrapText)
    uFlags |= DT_WORDBREAK ;
    , linefeeds quickly take effect.

    Reply
  • Possible problem with email address

    Posted by Legacy on 07/26/2000 12:00am

    Originally posted by: Steve Villarreal

    I really like this class.  However, it appears that if the m_strLinkTarget has an email address without the "mailto:", it will not work correctly.  I made the following minor change in CHyperlink::OnClick to fix this:
    
    

    Original:

    if (strTarget == "")
    {
    GetWindowText(strTarget);
    iLength = strMailTo.GetLength();
    if (strTarget.Find('@') != -1 &&
    strTarget.Left(iLength) != strMailTo)
    strTarget = strMailTo + strTarget ;
    }


    New:

    if (strTarget == "")
    {
    GetWindowText(strTarget);
    }

    iLength = strMailTo.GetLength();
    if (strTarget.Find('@') != -1 &&
    strTarget.Left(iLength) != strMailTo)
    strTarget = strMailTo + strTarget ;


    Steve

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • With JRebel, developers get to see their code changes immediately, fine-tune their code with incremental changes, debug, explore and deploy their code with ease (both locally and remotely), and ultimately spend more time coding instead of waiting for the dreaded application redeploy to finish. Every time a developer tests a code change it takes minutes to build and deploy the application. JRebel keeps the app server running at all times, so testing is instantaneous and interactive.

  • When it comes to desktops – physical or virtual – it's all about the applications. Cloud-hosted virtual desktops are growing fast because you get local data center-class security and 24x7 access with the complete personalization and flexibility of your own desktop. Organizations make five common mistakes when it comes to planning and implementing their application management strategy. This eBook tells you what they are and how to avoid them, and offers real-life case studies on customers who didn't let …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds