added the possibility to hide the tip window automatically when the mouse leave the...
[wxWidgets.git] / src / generic / tipwin.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/generic/tipwin.cpp
3 // Purpose: implementation of wxTipWindow
4 // Author: Vadim Zeitlin
5 // Modified by:
6 // Created: 10.09.00
7 // RCS-ID: $Id$
8 // Copyright: (c) 2000 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
9 // Licence: wxWindows license
10 ///////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 #ifdef __GNUG__
21 #pragma implementation "tipwin.h"
22 #endif
23
24 // For compilers that support precompilatixon, includes "wx/wx.h".
25 #include "wx/wxprec.h"
26
27 #ifdef __BORLANDC__
28 #pragma hdrstop
29 #endif
30
31 #ifndef WX_PRECOMP
32 #include "wx/dcclient.h"
33 #endif // WX_PRECOMP
34
35 #include "wx/tipwin.h"
36
37 #if wxUSE_TIPWINDOW
38
39 #include "wx/timer.h"
40 #include "wx/settings.h"
41
42 // ----------------------------------------------------------------------------
43 // constants
44 // ----------------------------------------------------------------------------
45
46 static const wxCoord TEXT_MARGIN_X = 3;
47 static const wxCoord TEXT_MARGIN_Y = 3;
48
49 // ----------------------------------------------------------------------------
50 // wxTipWindowView
51 // ----------------------------------------------------------------------------
52
53 // Viewer window to put in the frame
54 class WXDLLEXPORT wxTipWindowView : public wxWindow
55 {
56 public:
57 wxTipWindowView(wxWindow *parent);
58
59 // event handlers
60 void OnPaint(wxPaintEvent& event);
61 void OnMouseClick(wxMouseEvent& event);
62 void OnMouseMove(wxMouseEvent& event);
63
64 #if !wxUSE_POPUPWIN
65 void OnKillFocus(wxFocusEvent& event);
66 #endif // wxUSE_POPUPWIN
67
68 // calculate the client rect we need to display the text
69 void Adjust(const wxString& text, wxCoord maxLength);
70
71 private:
72 wxTipWindow* m_parent;
73
74 #if !wxUSE_POPUPWIN
75 long m_creationTime;
76 #endif // !wxUSE_POPUPWIN
77
78 DECLARE_EVENT_TABLE()
79 };
80
81 // ============================================================================
82 // implementation
83 // ============================================================================
84
85 // ----------------------------------------------------------------------------
86 // event tables
87 // ----------------------------------------------------------------------------
88
89 BEGIN_EVENT_TABLE(wxTipWindow, wxTipWindowBase)
90 EVT_LEFT_DOWN(wxTipWindow::OnMouseClick)
91 EVT_RIGHT_DOWN(wxTipWindow::OnMouseClick)
92 EVT_MIDDLE_DOWN(wxTipWindow::OnMouseClick)
93
94 #if !wxUSE_POPUPWIN
95 EVT_KILL_FOCUS(wxTipWindow::OnKillFocus)
96 EVT_ACTIVATE(wxTipWindow::OnActivate)
97 #endif // !wxUSE_POPUPWIN
98 END_EVENT_TABLE()
99
100 BEGIN_EVENT_TABLE(wxTipWindowView, wxWindow)
101 EVT_PAINT(wxTipWindowView::OnPaint)
102
103 EVT_LEFT_DOWN(wxTipWindowView::OnMouseClick)
104 EVT_RIGHT_DOWN(wxTipWindowView::OnMouseClick)
105 EVT_MIDDLE_DOWN(wxTipWindowView::OnMouseClick)
106
107 EVT_MOTION(wxTipWindowView::OnMouseMove)
108
109 #if !wxUSE_POPUPWIN
110 EVT_KILL_FOCUS(wxTipWindowView::OnKillFocus)
111 #endif // !wxUSE_POPUPWIN
112 END_EVENT_TABLE()
113
114 // ----------------------------------------------------------------------------
115 // wxTipWindow
116 // ----------------------------------------------------------------------------
117
118 wxTipWindow::wxTipWindow(wxWindow *parent,
119 const wxString& text,
120 wxCoord maxLength,
121 wxTipWindow** windowPtr,
122 wxRect *rectBounds)
123 #if wxUSE_POPUPWIN
124 : wxPopupTransientWindow(parent)
125 #else
126 : wxFrame(parent, -1, _T(""),
127 wxDefaultPosition, wxDefaultSize,
128 wxNO_BORDER | wxFRAME_NO_TASKBAR )
129 #endif
130 {
131 SetTipWindowPtr(windowPtr);
132 if ( rectBounds )
133 {
134 SetBoundingRect(*rectBounds);
135 }
136
137 // set colours
138 //
139 // VZ: why don't we use wxSystemSettings for !MSW? (FIXME)
140 #ifdef __WXMSW__
141 SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_INFOTEXT));
142 SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_INFOBK));
143 #else
144 SetForegroundColour(*wxBLACK);
145 SetBackgroundColour(*wxWHITE);
146 #endif
147
148 // set size, position and show it
149 m_view = new wxTipWindowView(this);
150 m_view->Adjust(text, maxLength);
151 m_view->SetFocus();
152
153 int x, y;
154 wxGetMousePosition(&x, &y);
155
156 // we want to show the tip below the mouse, not over it
157 //
158 // NB: the reason we use "/ 2" here is that we don't know where the current
159 // cursors hot spot is... it would be nice if we could find this out
160 // though
161 y += wxSystemSettings::GetMetric(wxSYS_CURSOR_Y) / 2;
162
163 #if wxUSE_POPUPWIN
164 Position(wxPoint(x, y), wxSize(0, 0));
165 Popup(m_view);
166 #else
167 Move(x, y);
168 Show(TRUE);
169 #endif
170 }
171
172 wxTipWindow::~wxTipWindow()
173 {
174 if ( m_windowPtr )
175 {
176 *m_windowPtr = NULL;
177 }
178 }
179
180 void wxTipWindow::OnMouseClick(wxMouseEvent& WXUNUSED(event))
181 {
182 Close();
183 }
184
185 #if wxUSE_POPUPWIN
186
187 void wxTipWindow::OnDismiss()
188 {
189 Close();
190 }
191
192 #else // !wxUSE_POPUPWIN
193
194 void wxTipWindow::OnActivate(wxActivateEvent& event)
195 {
196 if (!event.GetActive())
197 Close();
198 }
199
200 void wxTipWindow::OnKillFocus(wxFocusEvent& WXUNUSED(event))
201 {
202 // Under Windows at least, we will get this immediately
203 // because when the view window is focussed, the
204 // tip window goes out of focus.
205 #ifdef __WXGTK__
206 Close();
207 #endif
208 }
209
210 #endif // wxUSE_POPUPWIN // !wxUSE_POPUPWIN
211
212 void wxTipWindow::SetBoundingRect(const wxRect& rectBound)
213 {
214 m_rectBound = rectBound;
215 }
216
217 void wxTipWindow::Close()
218 {
219 if ( m_windowPtr )
220 {
221 *m_windowPtr = NULL;
222 m_windowPtr = NULL;
223 }
224
225 #if wxUSE_POPUPWIN
226 Show(FALSE);
227 Destroy();
228 #else
229 wxFrame::Close();
230 #endif
231 }
232
233 // ----------------------------------------------------------------------------
234 // wxTipWindowView
235 // ----------------------------------------------------------------------------
236
237 wxTipWindowView::wxTipWindowView(wxWindow *parent)
238 : wxWindow(parent, -1,
239 wxDefaultPosition, wxDefaultSize,
240 wxNO_BORDER)
241 {
242 // set colours
243 //
244 // VZ: why don't we use wxSystemSettings for !MSW? (FIXME)
245 #ifdef __WXMSW__
246 SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_INFOTEXT));
247 SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_INFOBK));
248 #else
249 SetForegroundColour(*wxBLACK);
250 SetBackgroundColour(*wxWHITE);
251 #endif
252
253 #if !wxUSE_POPUPWIN
254 m_creationTime = wxGetLocalTime();
255 #endif // !wxUSE_POPUPWIN
256
257 m_parent = (wxTipWindow*)parent;
258 }
259
260 void wxTipWindowView::Adjust(const wxString& text, wxCoord maxLength)
261 {
262 wxClientDC dc(this);
263 dc.SetFont(GetFont());
264
265 // calculate the length: we want each line be no longer than maxLength
266 // pixels and we only break lines at words boundary
267 wxString current;
268 wxCoord height, width,
269 widthMax = 0;
270 m_parent->m_heightLine = 0;
271
272 bool breakLine = FALSE;
273 for ( const wxChar *p = text.c_str(); ; p++ )
274 {
275 if ( *p == _T('\n') || *p == _T('\0') )
276 {
277 dc.GetTextExtent(current, &width, &height);
278 if ( width > widthMax )
279 widthMax = width;
280
281 if ( height > m_parent->m_heightLine )
282 m_parent->m_heightLine = height;
283
284 m_parent->m_textLines.Add(current);
285
286 if ( !*p )
287 {
288 // end of text
289 break;
290 }
291
292 current.clear();
293 breakLine = FALSE;
294 }
295 else if ( breakLine && (*p == _T(' ') || *p == _T('\t')) )
296 {
297 // word boundary - break the line here
298 m_parent->m_textLines.Add(current);
299 current.clear();
300 breakLine = FALSE;
301 }
302 else // line goes on
303 {
304 current += *p;
305 dc.GetTextExtent(current, &width, &height);
306 if ( width > maxLength )
307 breakLine = TRUE;
308
309 if ( width > widthMax )
310 widthMax = width;
311
312 if ( height > m_parent->m_heightLine )
313 m_parent->m_heightLine = height;
314 }
315 }
316
317 // take into account the border size and the margins
318 width = 2*(TEXT_MARGIN_X + 1) + widthMax;
319 height = 2*(TEXT_MARGIN_Y + 1) + m_parent->m_textLines.GetCount()*m_parent->m_heightLine;
320 m_parent->SetClientSize(width, height);
321 SetSize(0, 0, width, height);
322 }
323
324 void wxTipWindowView::OnPaint(wxPaintEvent& WXUNUSED(event))
325 {
326 wxPaintDC dc(this);
327
328 wxRect rect;
329 wxSize size = GetClientSize();
330 rect.width = size.x;
331 rect.height = size.y;
332
333 // first filll the background
334 dc.SetBrush(wxBrush(GetBackgroundColour(), wxSOLID));
335 dc.SetPen( wxPen(GetForegroundColour(), 1, wxSOLID) );
336 dc.DrawRectangle(rect);
337
338 // and then draw the text line by line
339 dc.SetTextBackground(GetBackgroundColour());
340 dc.SetTextForeground(GetForegroundColour());
341 dc.SetFont(GetFont());
342
343 wxPoint pt;
344 pt.x = TEXT_MARGIN_X;
345 pt.y = TEXT_MARGIN_Y;
346 size_t count = m_parent->m_textLines.GetCount();
347 for ( size_t n = 0; n < count; n++ )
348 {
349 dc.DrawText(m_parent->m_textLines[n], pt);
350
351 pt.y += m_parent->m_heightLine;
352 }
353 }
354
355 void wxTipWindowView::OnMouseClick(wxMouseEvent& WXUNUSED(event))
356 {
357 m_parent->Close();
358 }
359
360 void wxTipWindowView::OnMouseMove(wxMouseEvent& event)
361 {
362 const wxRect& rectBound = m_parent->m_rectBound;
363
364 if ( rectBound.width &&
365 !rectBound.Inside(ClientToScreen(event.GetPosition())) )
366 {
367 // mouse left the bounding rect, disappear
368 m_parent->Close();
369 }
370 else
371 {
372 event.Skip();
373 }
374 }
375
376 #if !wxUSE_POPUPWIN
377 void wxTipWindowView::OnKillFocus(wxFocusEvent& WXUNUSED(event))
378 {
379 // Workaround the kill focus event happening just after creation in wxGTK
380 if (wxGetLocalTime() > m_creationTime + 1)
381 m_parent->Close();
382 }
383 #endif // !wxUSE_POPUPWIN
384
385 #endif // wxUSE_TIPWINDOW