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