1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/generic/hyperlink.cpp
3 // Purpose:     Hyperlink control
4 // Author:      David Norris <danorris@gmail.com>, Otto Wyss
5 // Modified by: Ryan Norton, Francesco Montorsi
6 // Created:     04/02/2005
7 // RCS-ID:      $Id: hyperlink.cpp 62092 2009-09-24 16:46:50Z JS $
8 // Copyright:   (c) 2005 David Norris
9 // Licence:     wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11 
12 // ============================================================================
13 // declarations
14 // ============================================================================
15 
16 //---------------------------------------------------------------------------
17 // Pre-compiled header stuff
18 //---------------------------------------------------------------------------
19 
20 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
22 
23 #ifdef __BORLANDC__
24     #pragma hdrstop
25 #endif
26 
27 #if wxUSE_HYPERLINKCTRL
28 
29 //---------------------------------------------------------------------------
30 // Includes
31 //---------------------------------------------------------------------------
32 
33 #include "wx/hyperlink.h"
34 
35 #ifndef WX_PRECOMP
36     #include "wx/utils.h" // for wxLaunchDefaultBrowser
37     #include "wx/dcclient.h"
38     #include "wx/menu.h"
39     #include "wx/log.h"
40     #include "wx/dataobj.h"
41 #endif
42 
43 #include "wx/clipbrd.h"
44 
45 // ============================================================================
46 // implementation
47 // ============================================================================
48 
49 IMPLEMENT_DYNAMIC_CLASS(wxHyperlinkCtrl, wxControl)
50 IMPLEMENT_DYNAMIC_CLASS(wxHyperlinkEvent, wxCommandEvent)
51 DEFINE_EVENT_TYPE(wxEVT_COMMAND_HYPERLINK)
52 
53 // reserved for internal use only
54 #define wxHYPERLINKCTRL_POPUP_COPY_ID           16384
55 
56 const wxChar wxHyperlinkCtrlNameStr[] = wxT("hyperlink");
57 
58 // ----------------------------------------------------------------------------
59 // wxHyperlinkCtrl
60 // ----------------------------------------------------------------------------
61 
BEGIN_EVENT_TABLE(wxHyperlinkCtrl,wxControl)62 BEGIN_EVENT_TABLE(wxHyperlinkCtrl, wxControl)
63     EVT_PAINT(wxHyperlinkCtrl::OnPaint)
64     EVT_LEFT_DOWN(wxHyperlinkCtrl::OnLeftDown)
65     EVT_LEFT_UP(wxHyperlinkCtrl::OnLeftUp)
66     EVT_RIGHT_UP(wxHyperlinkCtrl::OnRightUp)
67     EVT_MOTION(wxHyperlinkCtrl::OnMotion)
68     EVT_LEAVE_WINDOW(wxHyperlinkCtrl::OnLeaveWindow)
69     EVT_SIZE(wxHyperlinkCtrl::OnSize)
70 
71     // for the context menu
72     EVT_MENU(wxHYPERLINKCTRL_POPUP_COPY_ID, wxHyperlinkCtrl::OnPopUpCopy)
73 END_EVENT_TABLE()
74 
75 bool wxHyperlinkCtrl::Create(wxWindow *parent, wxWindowID id,
76     const wxString& label, const wxString& url, const wxPoint& pos,
77     const wxSize& size, long style, const wxString& name)
78 {
79     wxASSERT_MSG(!url.empty() || !label.empty(),
80                  wxT("Both URL and label are empty ?"));
81 
82 #ifdef __WXDEBUG__
83     int alignment = (int)((style & wxHL_ALIGN_LEFT) != 0) +
84                     (int)((style & wxHL_ALIGN_CENTRE) != 0) +
85                     (int)((style & wxHL_ALIGN_RIGHT) != 0);
86     wxASSERT_MSG(alignment == 1,
87         wxT("Specify exactly one align flag!"));
88 #endif
89 
90     if (!wxControl::Create(parent, id, pos, size, style, wxDefaultValidator, name))
91         return false;
92 
93     // set to non empty strings both the url and the label
94     if(url.empty())
95         SetURL(label);
96     else
97         SetURL(url);
98 
99     if(label.empty())
100         SetLabel(url);
101     else
102         SetLabel(label);
103 
104     m_rollover = false;
105     m_clicking = false;
106     m_visited = false;
107 
108     // colours
109     m_normalColour = *wxBLUE;
110     m_hoverColour = *wxRED;
111     m_visitedColour = wxColour(wxT("#551a8b"));
112     SetForegroundColour(m_normalColour);
113 
114     // by default the font of an hyperlink control is underlined
115     wxFont f = GetFont();
116     f.SetUnderlined(true);
117     SetFont(f);
118 
119     SetInitialSize(size);
120 
121     return true;
122 }
123 
DoGetBestSize() const124 wxSize wxHyperlinkCtrl::DoGetBestSize() const
125 {
126     int w, h;
127 
128     wxClientDC dc((wxWindow *)this);
129     dc.SetFont(GetFont());
130     dc.GetTextExtent(GetLabel(), &w, &h);
131 
132     wxSize best(w, h);
133     CacheBestSize(best);
134     return best;
135 }
136 
137 
SetNormalColour(const wxColour & colour)138 void wxHyperlinkCtrl::SetNormalColour(const wxColour &colour)
139 {
140     m_normalColour = colour;
141     if (!m_visited)
142     {
143         SetForegroundColour(m_normalColour);
144         Refresh();
145     }
146 }
147 
SetVisitedColour(const wxColour & colour)148 void wxHyperlinkCtrl::SetVisitedColour(const wxColour &colour)
149 {
150     m_visitedColour = colour;
151     if (m_visited)
152     {
153         SetForegroundColour(m_visitedColour);
154         Refresh();
155     }
156 }
157 
DoContextMenu(const wxPoint & pos)158 void wxHyperlinkCtrl::DoContextMenu(const wxPoint &pos)
159 {
160     wxMenu *menuPopUp = new wxMenu(wxEmptyString, wxMENU_TEAROFF);
161     menuPopUp->Append(wxHYPERLINKCTRL_POPUP_COPY_ID, _("&Copy URL"));
162     PopupMenu( menuPopUp, pos );
163     delete menuPopUp;
164 }
165 
GetLabelRect() const166 wxRect wxHyperlinkCtrl::GetLabelRect() const
167 {
168     // our best size is always the size of the label without borders
169     wxSize c(GetClientSize()), b(GetBestSize());
170     wxPoint offset;
171 
172     // the label is always centered vertically
173     offset.y = (c.GetHeight()-b.GetHeight())/2;
174 
175     if (HasFlag(wxHL_ALIGN_CENTRE))
176         offset.x = (c.GetWidth()-b.GetWidth())/2;
177     else if (HasFlag(wxHL_ALIGN_RIGHT))
178         offset.x = c.GetWidth()-b.GetWidth();
179     else if (HasFlag(wxHL_ALIGN_LEFT))
180         offset.x = 0;
181     return wxRect(offset, b);
182 }
183 
184 
185 
186 // ----------------------------------------------------------------------------
187 // wxHyperlinkCtrl - event handlers
188 // ----------------------------------------------------------------------------
189 
OnPaint(wxPaintEvent & WXUNUSED (event))190 void wxHyperlinkCtrl::OnPaint(wxPaintEvent& WXUNUSED(event))
191 {
192     wxPaintDC dc(this);
193     dc.SetFont(GetFont());
194     dc.SetTextForeground(GetForegroundColour());
195     dc.SetTextBackground(GetBackgroundColour());
196 
197     dc.DrawText(GetLabel(), GetLabelRect().GetTopLeft());
198 }
199 
OnLeftDown(wxMouseEvent & event)200 void wxHyperlinkCtrl::OnLeftDown(wxMouseEvent& event)
201 {
202     // the left click must start from the hyperlink rect
203     m_clicking = GetLabelRect().Contains(event.GetPosition());
204 }
205 
OnLeftUp(wxMouseEvent & event)206 void wxHyperlinkCtrl::OnLeftUp(wxMouseEvent& event)
207 {
208     // the click must be started and ended in the hyperlink rect
209     if (!m_clicking || !GetLabelRect().Contains(event.GetPosition()))
210         return;
211 
212     SetForegroundColour(m_visitedColour);
213     m_visited = true;
214     m_clicking = false;
215 
216     // send the event
217     wxHyperlinkEvent linkEvent(this, GetId(), m_url);
218     if (!GetEventHandler()->ProcessEvent(linkEvent))     // was the event skipped ?
219         if (!wxLaunchDefaultBrowser(m_url))
220             wxLogWarning(wxT("Could not launch the default browser with url '%s' !"), m_url.c_str());
221 }
222 
OnRightUp(wxMouseEvent & event)223 void wxHyperlinkCtrl::OnRightUp(wxMouseEvent& event)
224 {
225     if( GetWindowStyle() & wxHL_CONTEXTMENU )
226         if ( GetLabelRect().Contains(event.GetPosition()) )
227             DoContextMenu(wxPoint(event.m_x, event.m_y));
228 }
229 
OnMotion(wxMouseEvent & event)230 void wxHyperlinkCtrl::OnMotion(wxMouseEvent& event)
231 {
232     wxRect textrc = GetLabelRect();
233 
234     if (textrc.Contains(event.GetPosition()))
235     {
236         SetCursor(wxCursor(wxCURSOR_HAND));
237         SetForegroundColour(m_hoverColour);
238         m_rollover = true;
239         Refresh();
240     }
241     else if (m_rollover)
242     {
243         SetCursor(*wxSTANDARD_CURSOR);
244         SetForegroundColour(!m_visited ? m_normalColour : m_visitedColour);
245         m_rollover = false;
246         Refresh();
247     }
248 }
249 
OnLeaveWindow(wxMouseEvent & WXUNUSED (event))250 void wxHyperlinkCtrl::OnLeaveWindow(wxMouseEvent& WXUNUSED(event) )
251 {
252     // NB: when the label rect and the client size rect have the same
253     //     height this function is indispensable to remove the "rollover"
254     //     effect as the OnMotion() event handler could not be called
255     //     in that case moving the mouse out of the label vertically...
256 
257     if (m_rollover)
258     {
259         SetCursor(*wxSTANDARD_CURSOR);
260         SetForegroundColour(!m_visited ? m_normalColour : m_visitedColour);
261         m_rollover = false;
262         Refresh();
263     }
264 }
265 
OnPopUpCopy(wxCommandEvent & WXUNUSED (event))266 void wxHyperlinkCtrl::OnPopUpCopy( wxCommandEvent& WXUNUSED(event) )
267 {
268 #if wxUSE_CLIPBOARD
269     if (!wxTheClipboard->Open())
270         return;
271 
272     wxTextDataObject *data = new wxTextDataObject( m_url );
273     wxTheClipboard->SetData( data );
274     wxTheClipboard->Close();
275 #endif // wxUSE_CLIPBOARD
276 }
277 
OnSize(wxSizeEvent & WXUNUSED (event))278 void wxHyperlinkCtrl::OnSize(wxSizeEvent& WXUNUSED(event))
279 {
280     // update the position of the label in the screen respecting
281     // the selected align flag
282     Refresh();
283 }
284 
285 #endif // wxUSE_HYPERLINKCTRL
286