wxScrolledWindow is now a native widget under GTK.
[wxWidgets.git] / src / gtk1 / scrolwin.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: generic/scrolwin.cpp
3 // Purpose: wxScrolledWindow implementation
4 // Author: Julian Smart
5 // Modified by:
6 // Created: 01/02/97
7 // RCS-ID: $Id$
8 // Copyright: (c) Julian Smart and Markus Holzem
9 // Licence: wxWindows license
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 #ifdef __GNUG__
21 #pragma implementation "scrolwin.h"
22 #endif
23
24 // For compilers that support precompilation, includes "wx.h".
25 #include "wx/wxprec.h"
26
27 #ifdef __BORLANDC__
28 #pragma hdrstop
29 #endif
30
31 #include "wx/utils.h"
32 #include "wx/dcclient.h"
33
34 #include "wx/gtk/scrolwin.h"
35 #include "wx/panel.h"
36
37 #include <gtk/gtk.h>
38 #include "wx/gtk/win_gtk.h"
39
40 // ----------------------------------------------------------------------------
41 // event tables
42 // ----------------------------------------------------------------------------
43
44 BEGIN_EVENT_TABLE(wxScrolledWindow, wxPanel)
45 EVT_SIZE(wxScrolledWindow::OnSize)
46 EVT_PAINT(wxScrolledWindow::OnPaint)
47 EVT_CHAR(wxScrolledWindow::OnChar)
48 END_EVENT_TABLE()
49
50 IMPLEMENT_DYNAMIC_CLASS(wxScrolledWindow, wxPanel)
51
52 // ============================================================================
53 // implementation
54 // ============================================================================
55
56 //-----------------------------------------------------------------------------
57 // data
58 //-----------------------------------------------------------------------------
59
60 extern bool g_blockEventsOnDrag;
61
62 //-----------------------------------------------------------------------------
63 // idle system
64 //-----------------------------------------------------------------------------
65
66 extern void wxapp_install_idle_handler();
67 extern bool g_isIdle;
68
69 //-----------------------------------------------------------------------------
70 // "value_changed" from m_vAdjust
71 //-----------------------------------------------------------------------------
72
73 static void gtk_scrolled_window_vscroll_callback( GtkAdjustment *adjust, wxScrolledWindow *win )
74 {
75 if (g_isIdle)
76 wxapp_install_idle_handler();
77
78 if (g_blockEventsOnDrag) return;
79
80 if (!win->m_hasVMT) return;
81
82 win->GtkVScroll( adjust->value );
83 }
84
85 //-----------------------------------------------------------------------------
86 // "value_changed" from m_hAdjust
87 //-----------------------------------------------------------------------------
88
89 static void gtk_scrolled_window_hscroll_callback( GtkAdjustment *adjust, wxScrolledWindow *win )
90 {
91 if (g_isIdle)
92 wxapp_install_idle_handler();
93
94 if (g_blockEventsOnDrag) return;
95 if (!win->m_hasVMT) return;
96
97 win->GtkHScroll( adjust->value );
98 }
99
100 //-----------------------------------------------------------------------------
101 // InsertChild for wxScrolledWindow
102 //-----------------------------------------------------------------------------
103
104 static void wxInsertChildInScrolledWindow( wxWindow* parent, wxWindow* child )
105 {
106 /* the window might have been scrolled already, do we
107 have to adapt the position */
108 GtkPizza *pizza = GTK_PIZZA(parent->m_wxwindow);
109 child->m_x += pizza->xoffset;
110 child->m_y += pizza->yoffset;
111
112 gtk_pizza_put( GTK_PIZZA(parent->m_wxwindow),
113 GTK_WIDGET(child->m_widget),
114 child->m_x,
115 child->m_y,
116 child->m_width,
117 child->m_height );
118 }
119
120 // ----------------------------------------------------------------------------
121 // wxScrolledWindow creation
122 // ----------------------------------------------------------------------------
123
124 wxScrolledWindow::wxScrolledWindow()
125 {
126 m_xScrollPixelsPerLine = 0;
127 m_yScrollPixelsPerLine = 0;
128 m_xScrollingEnabled = TRUE;
129 m_yScrollingEnabled = TRUE;
130 m_xScrollPosition = 0;
131 m_yScrollPosition = 0;
132 m_xScrollLines = 0;
133 m_yScrollLines = 0;
134 m_xScrollLinesPerPage = 0;
135 m_yScrollLinesPerPage = 0;
136 m_targetWindow = (wxWindow*) NULL;
137 }
138
139 bool wxScrolledWindow::Create(wxWindow *parent,
140 wxWindowID id,
141 const wxPoint& pos,
142 const wxSize& size,
143 long style,
144 const wxString& name)
145 {
146 if (!PreCreation( parent, pos, size ) ||
147 !CreateBase( parent, id, pos, size, style, wxDefaultValidator, name ))
148 {
149 wxFAIL_MSG( wxT("wxWindow creation failed") );
150 return FALSE;
151 }
152
153 m_insertCallback = wxInsertChildInScrolledWindow;
154
155 m_xScrollPixelsPerLine = 0;
156 m_yScrollPixelsPerLine = 0;
157 m_xScrollingEnabled = TRUE;
158 m_yScrollingEnabled = TRUE;
159 m_xScrollPosition = 0;
160 m_yScrollPosition = 0;
161 m_xScrollLines = 0;
162 m_yScrollLines = 0;
163 m_xScrollLinesPerPage = 0;
164 m_yScrollLinesPerPage = 0;
165
166 m_targetWindow = this;
167
168 m_widget = gtk_scrolled_window_new( (GtkAdjustment *) NULL, (GtkAdjustment *) NULL );
169 GTK_WIDGET_UNSET_FLAGS( m_widget, GTK_CAN_FOCUS );
170
171 GtkScrolledWindow *scrolledWindow = GTK_SCROLLED_WINDOW(m_widget);
172
173 GtkScrolledWindowClass *scroll_class = GTK_SCROLLED_WINDOW_CLASS( GTK_OBJECT_GET_CLASS(m_widget) );
174 scroll_class->scrollbar_spacing = 0;
175
176 gtk_scrolled_window_set_policy( scrolledWindow, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
177
178 m_hAdjust = gtk_range_get_adjustment( GTK_RANGE(scrolledWindow->hscrollbar) );
179 m_vAdjust = gtk_range_get_adjustment( GTK_RANGE(scrolledWindow->vscrollbar) );
180
181 m_wxwindow = gtk_pizza_new();
182
183 gtk_container_add( GTK_CONTAINER(m_widget), m_wxwindow );
184
185 GtkPizza *pizza = GTK_PIZZA(m_wxwindow);
186
187 if (HasFlag(wxRAISED_BORDER))
188 {
189 gtk_pizza_set_shadow_type( pizza, GTK_MYSHADOW_OUT );
190 }
191 else if (HasFlag(wxSUNKEN_BORDER))
192 {
193 gtk_pizza_set_shadow_type( pizza, GTK_MYSHADOW_IN );
194 }
195 else if (HasFlag(wxSIMPLE_BORDER))
196 {
197 gtk_pizza_set_shadow_type( pizza, GTK_MYSHADOW_THIN );
198 }
199 else
200 {
201 gtk_pizza_set_shadow_type( pizza, GTK_MYSHADOW_NONE );
202 }
203
204 GTK_WIDGET_SET_FLAGS( m_wxwindow, GTK_CAN_FOCUS );
205 m_acceptsFocus = TRUE;
206
207 // I _really_ don't want scrollbars in the beginning
208 m_vAdjust->lower = 0.0;
209 m_vAdjust->upper = 1.0;
210 m_vAdjust->value = 0.0;
211 m_vAdjust->step_increment = 1.0;
212 m_vAdjust->page_increment = 1.0;
213 gtk_signal_emit_by_name( GTK_OBJECT(m_vAdjust), "changed" );
214 m_hAdjust->lower = 0.0;
215 m_hAdjust->upper = 1.0;
216 m_hAdjust->value = 0.0;
217 m_hAdjust->step_increment = 1.0;
218 m_hAdjust->page_increment = 1.0;
219 gtk_signal_emit_by_name( GTK_OBJECT(m_hAdjust), "changed" );
220
221 // these handlers get notified when screen updates are required either when
222 // scrolling or when the window size (and therefore scrollbar configuration)
223 // has changed
224 gtk_signal_connect( GTK_OBJECT(m_hAdjust), "value_changed",
225 (GtkSignalFunc) gtk_scrolled_window_hscroll_callback, (gpointer) this );
226 gtk_signal_connect( GTK_OBJECT(m_vAdjust), "value_changed",
227 (GtkSignalFunc) gtk_scrolled_window_vscroll_callback, (gpointer) this );
228
229 gtk_widget_show( m_wxwindow );
230
231 if (m_parent)
232 m_parent->DoAddChild( this );
233
234 PostCreation();
235
236 Show( TRUE );
237
238 return TRUE;
239 }
240
241 wxScrolledWindow::~wxScrolledWindow()
242 {
243 }
244
245 // ----------------------------------------------------------------------------
246 // setting scrolling parameters
247 // ----------------------------------------------------------------------------
248
249 /*
250 * pixelsPerUnitX/pixelsPerUnitY: number of pixels per unit (e.g. pixels per text line)
251 * noUnitsX/noUnitsY: : no. units per scrollbar
252 */
253 void wxScrolledWindow::SetScrollbars (int pixelsPerUnitX, int pixelsPerUnitY,
254 int noUnitsX, int noUnitsY,
255 int xPos, int yPos, bool noRefresh )
256 {
257 m_xScrollPixelsPerLine = pixelsPerUnitX;
258 m_yScrollPixelsPerLine = pixelsPerUnitY;
259 m_xScrollPosition = xPos;
260 m_yScrollPosition = yPos;
261 m_xScrollLines = noUnitsX;
262 m_yScrollLines = noUnitsY;
263
264 m_hAdjust->lower = 0.0;
265 m_hAdjust->upper = noUnitsX;
266 m_hAdjust->value = xPos;
267 m_hAdjust->step_increment = 1.0;
268 m_hAdjust->page_increment = 1.0;
269
270 m_vAdjust->lower = 0.0;
271 m_vAdjust->upper = noUnitsY;
272 m_vAdjust->value = yPos;
273 m_vAdjust->step_increment = 1.0;
274 m_vAdjust->page_increment = 1.0;
275
276 AdjustScrollbars();
277 }
278
279 void wxScrolledWindow::AdjustScrollbars()
280 {
281 int w, h;
282 m_targetWindow->GetClientSize( &w, &h );
283
284 if (m_xScrollPixelsPerLine == 0)
285 m_hAdjust->page_size = 1.0;
286 else
287 m_hAdjust->page_size = (w / m_xScrollPixelsPerLine);
288
289 if (m_yScrollPixelsPerLine == 0)
290 m_vAdjust->page_size = 1.0;
291 else
292 m_vAdjust->page_size = (h / m_yScrollPixelsPerLine);
293
294 gtk_signal_emit_by_name( GTK_OBJECT(m_vAdjust), "changed" );
295 gtk_signal_emit_by_name( GTK_OBJECT(m_hAdjust), "changed" );
296 }
297
298 // ----------------------------------------------------------------------------
299 // target window handling
300 // ----------------------------------------------------------------------------
301
302 void wxScrolledWindow::SetTargetWindow( wxWindow *target )
303 {
304 wxASSERT_MSG( target, wxT("target window must not be NULL") );
305 m_targetWindow = target;
306 }
307
308 wxWindow *wxScrolledWindow::GetTargetWindow()
309 {
310 return m_targetWindow;
311 }
312
313 // Override this function if you don't want to have wxScrolledWindow
314 // automatically change the origin according to the scroll position.
315 void wxScrolledWindow::PrepareDC(wxDC& dc)
316 {
317 dc.SetDeviceOrigin( -m_xScrollPosition * m_xScrollPixelsPerLine,
318 -m_yScrollPosition * m_yScrollPixelsPerLine );
319 }
320
321 void wxScrolledWindow::GetScrollPixelsPerUnit (int *x_unit, int *y_unit) const
322 {
323 if ( x_unit )
324 *x_unit = m_xScrollPixelsPerLine;
325 if ( y_unit )
326 *y_unit = m_yScrollPixelsPerLine;
327 }
328
329 int wxScrolledWindow::GetScrollPageSize(int orient) const
330 {
331 if ( orient == wxHORIZONTAL )
332 return m_xScrollLinesPerPage;
333 else
334 return m_yScrollLinesPerPage;
335 }
336
337 void wxScrolledWindow::SetScrollPageSize(int orient, int pageSize)
338 {
339 if ( orient == wxHORIZONTAL )
340 m_xScrollLinesPerPage = pageSize;
341 else
342 m_yScrollLinesPerPage = pageSize;
343 }
344
345 /*
346 * Scroll to given position (scroll position, not pixel position)
347 */
348 void wxScrolledWindow::Scroll( int x_pos, int y_pos )
349 {
350 if (!m_targetWindow)
351 return;
352
353 if (((x_pos == -1) || (x_pos == m_xScrollPosition)) &&
354 ((y_pos == -1) || (y_pos == m_yScrollPosition))) return;
355
356 int w, h;
357 m_targetWindow->GetClientSize(&w, &h);
358
359 if ((x_pos != -1) && (m_xScrollPixelsPerLine))
360 {
361 int old_x = m_xScrollPosition;
362 m_xScrollPosition = x_pos;
363
364 // Calculate page size i.e. number of scroll units you get on the
365 // current client window
366 int noPagePositions = (int) ( (w/(double)m_xScrollPixelsPerLine) + 0.5 );
367 if (noPagePositions < 1) noPagePositions = 1;
368
369 // Correct position if greater than extent of canvas minus
370 // the visible portion of it or if below zero
371 m_xScrollPosition = wxMin( m_xScrollLines-noPagePositions, m_xScrollPosition );
372 m_xScrollPosition = wxMax( 0, m_xScrollPosition );
373
374 if (old_x != m_xScrollPosition) {
375 m_targetWindow->ScrollWindow( (old_x-m_xScrollPosition)*m_xScrollPixelsPerLine, 0 );
376 }
377 }
378 if ((y_pos != -1) && (m_yScrollPixelsPerLine))
379 {
380 int old_y = m_yScrollPosition;
381 m_yScrollPosition = y_pos;
382
383 // Calculate page size i.e. number of scroll units you get on the
384 // current client window
385 int noPagePositions = (int) ( (h/(double)m_yScrollPixelsPerLine) + 0.5 );
386 if (noPagePositions < 1) noPagePositions = 1;
387
388 // Correct position if greater than extent of canvas minus
389 // the visible portion of it or if below zero
390 m_yScrollPosition = wxMin( m_yScrollLines-noPagePositions, m_yScrollPosition );
391 m_yScrollPosition = wxMax( 0, m_yScrollPosition );
392
393 if (old_y != m_yScrollPosition) {
394 m_targetWindow->ScrollWindow( 0, (old_y-m_yScrollPosition)*m_yScrollPixelsPerLine );
395 }
396 }
397 }
398
399 void wxScrolledWindow::GtkVScroll( float value )
400 {
401 Scroll( -1, (int)(value+0.5) );
402 }
403
404 void wxScrolledWindow::GtkHScroll( float value )
405 {
406 Scroll( (int)(value+0.5), -1 );
407 }
408
409 void wxScrolledWindow::EnableScrolling (bool x_scroll, bool y_scroll)
410 {
411 m_xScrollingEnabled = x_scroll;
412 m_yScrollingEnabled = y_scroll;
413 }
414
415 void wxScrolledWindow::GetVirtualSize (int *x, int *y) const
416 {
417 if ( x )
418 *x = m_xScrollPixelsPerLine * m_xScrollLines;
419 if ( y )
420 *y = m_yScrollPixelsPerLine * m_yScrollLines;
421 }
422
423 // Where the current view starts from
424 void wxScrolledWindow::GetViewStart (int *x, int *y) const
425 {
426 if ( x )
427 *x = m_xScrollPosition;
428 if ( y )
429 *y = m_yScrollPosition;
430 }
431
432 void wxScrolledWindow::CalcScrolledPosition(int x, int y, int *xx, int *yy) const
433 {
434 if ( xx )
435 *xx = x - m_xScrollPosition * m_xScrollPixelsPerLine;
436 if ( yy )
437 *yy = y - m_yScrollPosition * m_yScrollPixelsPerLine;
438 }
439
440 void wxScrolledWindow::CalcUnscrolledPosition(int x, int y, int *xx, int *yy) const
441 {
442 if ( xx )
443 *xx = x + m_xScrollPosition * m_xScrollPixelsPerLine;
444 if ( yy )
445 *yy = y + m_yScrollPosition * m_yScrollPixelsPerLine;
446 }
447
448 // ----------------------------------------------------------------------------
449 // event handlers
450 // ----------------------------------------------------------------------------
451
452 // Default OnSize resets scrollbars, if any
453 void wxScrolledWindow::OnSize(wxSizeEvent& WXUNUSED(event))
454 {
455 #if wxUSE_CONSTRAINTS
456 if (GetAutoLayout())
457 Layout();
458 #endif
459
460 AdjustScrollbars();
461 }
462
463 // This calls OnDraw, having adjusted the origin according to the current
464 // scroll position
465 void wxScrolledWindow::OnPaint(wxPaintEvent& WXUNUSED(event))
466 {
467 wxPaintDC dc(this);
468 PrepareDC(dc);
469
470 OnDraw(dc);
471 }
472
473 // kbd handling: notice that we use OnChar() and not OnKeyDown() for
474 // compatibility here - if we used OnKeyDown(), the programs which process
475 // arrows themselves in their OnChar() would never get the message and like
476 // this they always have the priority
477 void wxScrolledWindow::OnChar(wxKeyEvent& event)
478 {
479 int stx, sty, // view origin
480 szx, szy, // view size (total)
481 clix, cliy; // view size (on screen)
482
483 ViewStart(&stx, &sty);
484 GetClientSize(&clix, &cliy);
485 GetVirtualSize(&szx, &szy);
486
487 if( m_xScrollPixelsPerLine )
488 {
489 clix /= m_xScrollPixelsPerLine;
490 szx /= m_xScrollPixelsPerLine;
491 }
492 else
493 {
494 clix = 0;
495 szx = -1;
496 }
497 if( m_yScrollPixelsPerLine )
498 {
499 cliy /= m_yScrollPixelsPerLine;
500 szy /= m_yScrollPixelsPerLine;
501 }
502 else
503 {
504 cliy = 0;
505 szy = -1;
506 }
507
508 int dsty;
509 switch ( event.KeyCode() )
510 {
511 case WXK_PAGEUP:
512 case WXK_PRIOR:
513 dsty = sty - (5 * cliy / 6);
514 Scroll(-1, (dsty == -1) ? 0 : dsty);
515 break;
516
517 case WXK_PAGEDOWN:
518 case WXK_NEXT:
519 Scroll(-1, sty + (5 * cliy / 6));
520 break;
521
522 case WXK_HOME:
523 Scroll(0, event.ControlDown() ? 0 : -1);
524 break;
525
526 case WXK_END:
527 Scroll(szx - clix, event.ControlDown() ? szy - cliy : -1);
528 break;
529
530 case WXK_UP:
531 Scroll(-1, sty - 1);
532 break;
533
534 case WXK_DOWN:
535 Scroll(-1, sty + 1);
536 break;
537
538 case WXK_LEFT:
539 Scroll(stx - 1, -1);
540 break;
541
542 case WXK_RIGHT:
543 Scroll(stx + 1, -1);
544 break;
545
546 default:
547 // not for us
548 event.Skip();
549 }
550 }