Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

TitleTips Support for Edit, ListBox and ComboBox Controls

0.00/5 (No votes)
6 Mar 2008 1  
An article on a mix-in class to support TitleTips for Edit, ListBox and ComboBox controls

Updates

Even though it is a tiny but handy utility class to support TitleTip for Edit, ListBox and ComboBox controls with only a few lines of code, it has been limited in usage due to ATL/WTL dependency. Most of all, it exhibits a problem in non-WIN32/x86 systems (i.e. x64) since the custom ATL-style thunk (MessageHook.hpp) -- which I used in the original code -- is not NX/DEP-compatible. It can thus throw an exception in the 64-bit Windows system.

x64 Support and NX/DEP Compatibility

I completely rewrote the instance sub-classing code to deprecate the old custom thunk, and now it steals the thunk definition from ATL. As result, it is NX/DEP compatible if compiled against ATL8 or higher and can support various platforms (x86/x64/IA64/alpha...) that ATL supports. Even though it is designed to compile against ATL3 or ATL7 as well, such binaries will suffer from all the issues which legacy ATL binaries do nowadays and are not NX/DEP compatible.

Stealing the ATL thunk definition does not imply ATL dependency at the binary level, but only at the source level. Two ATL header files, atlbase.h and atlwin.h, will be included into the project. If not done yet, when MessageHook.hpp is being included.

C API-style DLL Version Porting

By encapsulating class into DLL and exporting C-style API functions, it has potential to be used in many other programming languages, not only in C++. It is implemented as a handle/body idiom. The DLL binaries attached here are compiled against ATL8 and are thus NX/DEP compatible. Alternatively, you can create your own DLL for the platform of your interest using the DLL project called "TipTMan"' in the attached demo project files. Please note that the method of deriving from CTitleTipHandlerT<T> in C++ is still valid and works well.

C API Example

Using DLL functions is as simple as shown below:

#include "TTipMan.h"
# pragma message("Auto link to TTipMan.lib")
# pragma comment(lib, "TTipMan.lib")

BOOL CTestDlg::OnInitDialog()
{
  CDialog::OnInitDialog();

  // Create TitleTip handler and hook into dialog window
  HANDLE hTTMan = CreateTitleTipHandler( GetSafeHwnd() );

  // Add TitleTip support for edit control (m_Edit)
  // Optionally set yellow as foreground and black as background color of the tip
  AddTitleTipEdit( hTTMan, m_Edit.GetSafeHwnd(), RGB(255, 255, 0), RGB(0, 0, 0) );

  // ...
}

CreateTitleTipHandler() and DestroyTitleTipHandler() have been added to implement handle/body idioms in the DLL version and all the existing functions are modified to take the additional HANDLE returned by CreateTitleTipHandler().

C Functions Exported from DLL

#define COLOR_DEFAULT (DWORD)0xffffffff

/**
 *  Create a TitleTip manager and subclass the specified parent window which
 *  will receive TTN_xxx notification from target controls to support TitleTips
 *
 *  \param   hwnd [in]  HWND Specify the handle to the parent window of controls
 *
 *  \return HANDLE Return the handle for the TitleTip manager to manage TitleTips
**/
HANDLE WINAPI CreateTitleTipHandler(HWND hwnd);

/**
 *  Destroy the TitleTip manager and perform all related clean-ups 
 *  including unsubclassing.
 *  Not like ordinary handles, it is not compulsory to call this function as it will 
 *  always
 *  be destroyed and cleaned up when the subclassed parent window receives the final
 *  message(WM_NCDESTROY)
 *
 *  \param   handle [in]  HANDLE  Specify the handle to the TitleTip manager to be 
 *    destroyed
 *
 *  \return  Return void
**/
void WINAPI DestroyTitleTipHandler(HANDLE handle);

BOOL WINAPI AddTitleTipEdit(HANDLE handle, HWND hwndEdit,
              COLORREF crTipTextColor = COLOR_DEFAULT, 
              COLORREF crTipBkColor = COLOR_DEFAULT);
void WINAPI DelTitleTipEdit(HANDLE handle, HWND hwndEdit);

BOOL WINAPI AddTitleTipListBox(HANDLE handle, HWND hwndListBox,
              COLORREF crTipTextColor = COLOR_DEFAULT, 
              COLORREF crTipBkColor = COLOR_DEFAULT,
              COLORREF crTipTextColorSelection = COLOR_DEFAULT, 
              COLORREF crTipBkColorSelection = COLOR_DEFAULT);
void WINAPI DelTitleTipListBox(HANDLE handle, HWND hwndListBox);

BOOL WINAPI AddTitleTipComboBox(HANDLE handle, HWND hwndComboBox,
              COLORREF crTipTextColor = COLOR_DEFAULT, 
              COLORREF crTipBkColor = COLOR_DEFAULT,
              COLORREF crTipTextColorSelection = COLOR_DEFAULT, 
              COLORREF crTipBkColorSelection = COLOR_DEFAULT);
void WINAPI DelTitleTipComboBox(HANDLE handle, HWND hwndComboBox);

HWND WINAPI GetHwndToolTip(HANDLE handle);

LPTSTR WINAPI GetToolTipBuf(HANDLE handle);
UINT WINAPI GetToolTipBufLen(HANDLE handle);
void WINAPI ResetToolTipBuf(HANDLE handle);
LPTSTR WINAPI ReallocToolTipBuf(HANDLE handle, UINT nLen);

-- End of Updates --

Introduction

While both the List View control and the Tree View control have easy-to-use built-in support for TitleTips (ToolTips), the other most commonly used three (Edit, List Box and Combo Box controls) don't have similar support at all. You need to create your own support using a ToolTip control. "TitleTips are tips that elongate a truncated item in a control so that you can see all of the item. For instance, Visual Studio has TitleTips in its Project Workspace. If a class name in the Project Workspace is too long to see all of it, a TitleTip appears that displays the full text. This makes it possible to use the Project Workspace without scrolling horizontally and without having to make it wider." For basic and more detail about ToolTips and TitleTips, refer to this article in MSJ.

When I was writing a small code to support TitleTips for the Edit control and List Box control in my Dialog Box, I hit upon the idea that it might be possible to create a generic mix-in class to support TitleTips for these controls. Actually, it wasn't as simple as I thought it would be in the beginning, since the ToolTip control's behavior for *fine* tuning wasn't well documented in MSDN nor anywhere else and, even worse, it behaves slightly differently according to the common control's version. Sigh~ .0

Background

The CMessageHook class was intensively used while implementing this mix-in class. The CMessageHook class is my own small but handy class to subclass window message procedures. It uses an assembly thunk technique that is identical to that of ATL, but purely WIN32. Thus, it can be used in any WIN32 platform (Intel CPU only). I wrote this class myself to understand the ins-and-outs of ATL and the thunk technique and I have used it in my several projects successively. I would rather say that I copied-'n-pasted the good features of a similar assembly's thunk implementations of others into my class :)

Now back to the TitleTip issue: I might have been able to implement it using a tracking ToolTip control (TTF_TRACK flag). Then I would implement my own custom timer and WM_MOUSEMOVE message handler, activate/deactivate tips (TTM_TRACKACTIVATE) after positioning tips on the right location and all that. There is very good example of this at The Code Project by Hans Dietrich. Instead, however, I decided to stick to the traditional TTN_GETDISPINFO (which used to be TTN_NEEDTEXT in olden times) and TTN_SHOW notification handling method simply because I am used to this method. Therefore I could leverage the built-in functions of the ToolTip control such as timer (TTM_SETDELAYTIME), automatic subclassing (TTF_SUBCLASS) and so on. That is why I thought it would be as simple as writing regular ToolTips. Unfortunately, I was wrong and I will explain the problems with which I was confronted during my implementation below.

Implementation Note

1. Using the TTF_TRANSPARENT Flag

As a nature of TitleTips, the tip will be laid over the tool window completely when it is being shown. In other words, at the moment of a tip's appearance, the tool window will stop receiving mouse messages. In obedience to this, it sends a TTM_POP message to the tip. In turn, the tip will be disappear, which makes the tool window start to receive mouse messages again. This causes the tip to be shown again and again and again.

We will see the tip blinking like crazy. The TTF_TRANSPARENT flag comes into play to solve this problem and, when specified while adding tool info (TOOLINFO.uFlags), it makes the tip re-direct certain mouse messages to the tool window so that the tool window can see the mouse message. This makes the tip remaining to be shown on the screen without blinking, even though the tool window is completely covered by the tip window.

Now you can try it on a Win2K or Win98 machine. The tip will be shown nicely and stay without blinking. You can click the left mouse button on the tip and it will disappear. Thus, you can perform a regular task (selecting a range of text, choosing a list item and so on).

Ok fine, now you can try it on WinXP with common control version 6. The story goes the same until you try to click on the tip. Unfortunately, the tip will remain shown on the screen even though you clicked, double-clicked or triple-clicked it. Not being disappeared on WinXP is very annoying, so I decided to change this behavior so that it becomes of one accord, regardless of platforms.

class CToolTipCtrlHook : public CMessageHook
{
public:
 // c'tor

 CToolTipCtrlHook(BOOL bAutoDelete) : CMessageHook(bAutoDelete) { }

protected:
 // implementations

protected:
 virtual BOOL ProcessWindowMessage(UINT uMsg, WPARAM wParam,
  LPARAM lParam, LRESULT &lResult)
 {
  BOOL bHandled = FALSE;

  switch(uMsg)
  {
  case WM_LBUTTONDOWN:
  case WM_MBUTTONDOWN:
  case WM_RBUTTONDOWN:
  case WM_XBUTTONDOWN:
   {
    /////////////////////////////////////////////////////

    // added since TTF_TRANSPARENT flag doesn't work well

    // especially in WinXP Common Control 6

    /////////////////////////////////////////////////////


    TOOLINFO ti;
    ti.cbSize = sizeof(TOOLINFO);
    // Get current tool info (TOOLINFO.uId is tool window's handle)

    // GetHwnd() return the subclassed ToolTip control's handle

    ::SendMessage(GetHwnd(), TTM_GETCURRENTTOOL, 0, (LPARAM)&ti);

    // pop (hide) current tip

    ::SendMessage(GetHwnd(), TTM_POP, 0, 0);

    // convert the current cursor position from tip window

    // coordinate to tool window coordinate

    POINT ptTarget = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
    ::ClientToScreen(GetHwnd(), &ptTarget);
    ::ScreenToClient((HWND)ti.uId, &ptTarget);

    // re-direct the mouse message to tool window

    lResult = ::SendMessage((HWND)ti.uId, uMsg, wParam,
     MAKELPARAM(ptTarget.x, ptTarget.y));

    bHandled = TRUE;
   }
   break;
  }

  return bHandled;
 }
};

2. Handling the TTN_GETDISPINFO Notification

TitleTips are different from regular tips in the way that they appear only if the length of text on tool window being displayed is wider than its displaying area (mostly the client rectangle of the control window) so that some portion of the text is being clipped. If the tool window can show complete text without any clipping, TitleTip should not be shown. In the moment of receiving TTN_SHOW, it is too late to determine the to show or not to show tip and the tip will be shown somewhere onscreen at least once, even though you try to pop it right away in the TTN_SHOW notification handler (it will blink).

In the TTN_GETDISPINFO notification hander, NMTTDISPINFO.lpszText needs to be set to a pointer to a private buffer for the text displayed in tip. NMTTDISPINFO.szText is limited to being used for text less than 80 chars in its length. Thus, it is not suitable for our purpose. So, if we don't want to show the tip onscreen, we can simply set NULL to NMTTDISPINFO.lpszText -- in the case of the text being clipped -- in the TTN_GETDISPINFO notification handler and return.

It sounds very simple, but there is a hidden pitfall in this simple method. Let's say we have only one edit control in our dialog box and we installed only one tip for the only edit control we have. If the edit control's text is being clipped, we can see the tip pop and pop up as intended when we move the mouse cursor over or leave the mouse cursor from the edit control. However, in the case that the text is not being clipped, our TTN_GETDISPINOF notification handler will set NMTTDISPINFO.lpszText as NULL and return. As a result, we won't be able to see any tip onscreen.

Let's type more characters in the edit control so that the text gets clipped again. Moving the cursor over the edit control, you will be surprised that no tip is popped up anymore, even though the text is being clipped (basically, no more TTN_GETDISPINFO notifications are issued). If we have more than one control and have installed more than one tip, then we can move the mouse cursor over from one tool window to the other tool window. This will cause the TTN_GETDISPINFO notification to be issued again when some condition has been met. However, you will still see in some cases that the tip doesn't pop again as it should.

I spent quite a large amount of time on finding out the solution to this and, ironically, it was found to be so simple when I finally got it. You need to *post* a TTM_POP message to the ToolTip control window in order to pop the tip explicitly if you set NMTTDISPINFO.lpszText as NULL in the TTN_GETDISPINFO notification handler. I emphasized the word *post* since if you send a message using ::SendMessage() instead of using ::PostMessage() in the TTN_GETDISPINFO notification handler, it will not work well in WinXP common control version 6.

LRESULT OnToolTipNotifyGetDispInfo(int nID,
 LPNMHDR lpnmhdr, BOOL& bHandled)
{
 bHandled = TRUE;

 LPNMTTDISPINFO lpnmtdi= reinterpret_cast<LPNMTTDISPINFO>(lpnmhdr);

 HWND hwndTool = reinterpret_cast<HWND>(lpnmtdi->hdr.idFrom);

 // to determine if the text is being clipped


 ...

 if(m_bTextClipped)
 {
  // to show TitleTip

  // to copy the text to display into my private buffer


  ...

  lpnmhdr.lpszText = pszMyPrivateBufer;
 }
 else
 {
  // not to show TitleTip

  lpnmhdr.lpszText = NULL;
  ::PostMessage(m_hwndToolTip, TTM_POP, 0, 0L);
 }

 return 0;
}

3. Sharing a ToolTip Control and Private Storage for Tip Text

We can not only have as many ToolTip control windows as we want, but we can also have as many private storage spaces for the corresponding individual ToolTip control. But think again. Do we really use more than one ToolTip control at an any given moment? No. Only one tip whose tool window the mouse cursor is on will be activated. So, why don't we just use only one ToolTip control window and private storage for all the TitleTip support and even give you the chance to share them in any place you want? Let's save some resource! Regardless of how many tool windows exist, one and only ToolTip control window and private storage for tip text will be created globally in my mix-in class. I used the generic singleton technique to implement this.

Use the following public member function of the CTitleTipHandler<T> template class to share the ToolTip control window and private storage in the same dialog, which is derived from the CTitleTipHandler<T> template class (mixed-in class):

// return ToolTip control

CToolTipCtrl &GetToolTipCtrl();

// return the pointer to the buffer (private

// storage) for tip text

LPTSTR GetToolTipBuf();

// return the size of buffer in TCHARs

int GetToolTipBufLen() const;

// zero'ing the buffer

void ResetToolTipBuf();

// delete old buffer and reallocate new buffer of size 'nLen' in heap memory

LPTSTR ReallocToolTipBuf(int nLen);

...or use the following counterpart public class member function of the CSSTip singleton class to share the ToolTip control window and private storage in any place:

using codeproject::CSSTip;

// return ToolTip control

static CToolTipCtrl &CSSTip::ToolTipCtrl();

// return the pointer to the buffer (private storage) for tip text

static LPTSTR CSSTip::GetToolTipBuf();

// return the size of buffer in TCHARs

static int CSSTip::GetToolTipBufLen() const;

// zero'ing the buffer

static void CSSTip::ResetToolTipBuf();

// delete old buffer and reallocate new buffer of

// size 'nLen' in heap memory

static LPTSTR CSSTip::ReallocToolTipBuf(int nLen);

4. Nested Instance Subclassing Issue

As I mentioned earlier, I intensively used my own handy CMessageHook class to subclass window procedures. Any control that will get TitleTip support from my mix-in class will be subclassed when its tool info is being added to the ToolTip control. If you are using a control window attached to the ATL::CWindowImpl<T>-derived class, the control is also subclassed internally by ATL implementation. When two or more instance subclassing occasions are involved, their subclass and unsubclass order becomes very important. The subclasses must be removed in reverse of the order in which they were performed. Otherwise, you will screw yourself.

Usually, an instance subclassing class implementator takes such a situation into his/her implementation consideration and puts minimal safety into their implementation. For example, CMessageHook doesn't delete itself when the subclass/unsubclass order gets mixed up and start parasitizing to windows it subclassed (it automatically deletes itself on receiving the WM_NCDESTROY message later). However, it is a really bad idea to mix up subclass and unsubclass order anyway, so remember and try to avoid it by some means or other.

class CListBoxImpl : public CWindowImpl<CListBoxImpl, CListBox>
{
public:
 BEGIN_MSG_MAP(CListBoxImpl)
 END_MSG_MAP()

 // NOP

};

using codeproject::CTitleTipHandler;

class CMainDlg : public CDialogImpl<CMainDlg>,
 public CTitleTipHandler<CMainDlg>
{
public:
 enum { IDD = IDD_MAINDLG };

 BEGIN_MSG_MAP(CMainDlg)
  CHAIN_MSG_MAP(CTitleTipHandler<CMainDlg>)
  MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
  ...
 END_MSG_MAP()

protected:
 CListBoxImpl c_lbList;

public:
 LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/,
  LPARAM /*lParam*/, BOOL& /*bHandled*/)
 {
  ...
  HWND hwndList = NULL;
  { // instance subclassing level 1 BEGIN

   c_lbList.SubclassWindow(GetDlgItem(IDC_LIST));
   hwndList = c_lbList.UnsubclassWindow();
  } // instance subclassing level 1 END


  { // instance subclassing level 1 BEGIN

   c_lbList.SubclassWindow(hwndList);

   c_lbList.AddString(_T(
    "0. Long String - TitleTip for EDIT, LISTBOX & COMBOBOX controls"));
   c_lbList.AddString(_T("1. Short String - Hello"));
   c_lbList.AddString(_T("2. Short String - Hello"));

   { // instance subclassing level 2 BEGIN

    AddTitleTipListBox(c_lbList);
    DelTitleTipListBox(c_lbList);
   } // instance subclassing level 2 END


   hwndList = c_lbList.UnsubclassWindow();
  } // instance subclassing level 1 END


  { // instance subclassing level 1 BEGIN

   AddTitleTipListBox(hwndList);
   { // instance subclassing level 2 BEGIN

    c_lbList.SubclassWindow(hwndList);
    c_lbList.AddString(_T(
     "3. Long String - TitleTip for EDIT, LISTBOX & COMBOBOX controls"));
    c_lbList.AddString(_T(
     "4. Long String - TitleTip for EDIT, LISTBOX & COMBOBOX controls"));
   }
  }
  ...
  return TRUE;

 }
 ...
}

Using the Code

It is very easy to use. The first thing to do is add CTitleTipHandler<T> as a base class of your dialog class (mixed-in) and then chain to the message map in CTitleTipHandler<T>. Use the AddTitleTipXXX(HWND hwndTool) functions or the DelTitleTipXXX(HWND hwndTool) functions respectively to add or to remove TitleTips support for the specific control (hwndTool) in your dialog initialization code (OnInitDialog()).

using codeproject::CTitleTipHandler;

class CMainDlg : public CDialogImpl<CMainDlg>,
 public CTitleTipHandler<CMainDlg>
{
public:
 enum { IDD = IDD_MAINDLG };

 BEGIN_MSG_MAP(CMainDlg)
  // chain to message map in CTitleTipHandler<CMainDlg>

  CHAIN_MSG_MAP(CTitleTipHandler<CMainDlg>)
  MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
  ...
 END_MSG_MAP()

protected:
 CEdit c_edEdit;
 CListBox c_lbList;
 CComboBox c_cbCombo;

public:
 LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/,
  LPARAM /*lParam*/, BOOL& /*bHandled*/)
 {
  ...
  c_edEdit.Attach(GetDlgItem(IDC_EDIT1));
  c_lbListBox.Attach(GetDlgItem(IDC_LIST1));
  c_cbComboBox.Attach(GetDlgItem(IDC_COMBO1));

  // TitleTip support for IDC_EDIT1 Edit control

  // Tip color (blue text and white background)

  AddTitleTipEdit(c_edEdit.m_hWnd, RGB(0, 0, 255), RGB(255, 255, 255));

  // TitleTip support for IDC_LIST1 ListBox control

  // Tip color (black text / white background),

  // selected item (red text / black background)

  AddTitleTipListBox(c_lbList.m_hWnd, RGB(0, 0, 0),
   RGB(255, 255, 255), RGB(255, 0, 0), RGB(0, 0, 0));

  // TitleTip support for IDC_COMBO1 ComboBox control

  // Tip color (COLOR_WINDOWTEXT / COLOR_WINDOW),

  // selected item (COLOR_HIGHLIGHTTEXT / COLOR_HIGHLIGHT)

  AddTitleTipComboBox(c_cbCombo.m_hWnd);
  ...
  return TRUE;

 }
 ...
}

Public Functions

BOOL AddTitleTipEdit(HWND hwndEdit, COLORREF crTipTextColor = COLORREF_DEFAULT, COLORREF crTipBkColor = COLORREF_DEFAULT);

  • Add TitleTip support for the specified Edit control (hwndEdit).
  • Default color - tip text: ::GetSysColor(COLOR_WINDOWTEXT) and tip background: ::GetSysColor(COLOR_WINDOW).
  • Return TRUE if successful.

void DelTitleTipEdit(HWND hwndEdit);

  • Remove TitleTip support for the specified Edit control (hwndEdit).

BOOL AddTitleTipListBox(HWND hwndListBox, COLORREF crTipTextColor = COLORREF_DEFAULT, COLORREF crTipBkColor = COLORREF_DEFAULT, COLORREF crTipTextColorSelection = COLOR_DEFAULT, COLORREF crTipBkColorSelection = COLOR_DEFAULT);

  • Add TitleTip support for the specified ListBox control (hwndListBox).
  • Default color - tip text: ::GetSysColor(COLOR_WINDOWTEXT), tip background: ::GetSysColor(COLOR_WINDOW), tip text (selected): ::GetSysColor(COLOR_HIGHLIGHTTEXT) and tip background (selected): ::GetSysColor(COLOR_HIGHLIGHT).
  • Return TRUE if successful.

void DelTitleTipListBox(HWND hwndListBox);

  • Remove TitleTip support for the specified ListBox control (hwndListBox).

BOOL AddTitleTipComboBox(HWND hwndComboBox, COLORREF crTipTextColor = COLORREF_DEFAULT, COLORREF crTipBkColor = COLORREF_DEFAULT, COLORREF crTipTextColorSelection = COLOR_DEFAULT, COLORREF crTipBkColorSelection = COLOR_DEFAULT);

  • Add TitleTip support for the specified ComboBox control (hwndComboBox).
  • Default color - tip text: ::GetSysColor(COLOR_WINDOWTEXT), tip background: ::GetSysColor(COLOR_WINDOW), tip text (selected): ::GetSysColor(COLOR_HIGHLIGHTTEXT) and tip background (selected): ::GetSysColor(COLOR_HIGHLIGHT).
  • Return TRUE if successful.

void DelTitleTipComboBox(HWND hwndComboBox);

  • Remove TitleTip support for the specified ComboBox control (hwndComboBox).

CToolTipCtrl &GetToolTipCtrl();

  • Return the ToolTip control.

LPTSTR GetToolTipBuf();

  • Return the pointer to the buffer (private storage) for tip text.

int GetToolTipBufLen() const

  • Return the size of the buffer in TCHARs.

void ResetToolTipBuf();

  • Zeroing the buffer.

LPTSTR ReallocToolTipBuf(int nLen);

  • Delete the old buffer and reallocate a new buffer of size nLen in heap memory.

Requirement

Internet Explorer 5 or later installed - to use TTM_ADJUSTRECT message.

History

Version 1.10 - 02/14/2008

  • Modified c'tor to accept AutoDelete flag for DLL version porting.
  • Bug-fix: EM_GETLINE - copied lines do not contain a null character.
  • Bug-fix: size of TOOLINFO struct can be different according to the version of the common control used. The correct size is 44 bytes for Win 2000 and Win XP without common controls 6.0, and 48 bytes for XP with common controls 6.0

Version 1.01 - 10/26/2004

  • Optimized to make it respond faster (lookup map only once) .
  • Fixed all the resource leaks found.
  • Some bugs fixed (Edit control related).

Version 1.0 - 10/08/2004

  • Initial release on The Code Project.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here