Native wxScrolledWindow finishing touches.
[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 if ((x_pos != -1) && (m_xScrollPixelsPerLine))
357 {
358 int old_x = m_xScrollPosition;
359 m_xScrollPosition = x_pos;
360 m_hAdjust->value = x_pos;
361
362 m_targetWindow->ScrollWindow( (old_x-m_xScrollPosition)*m_xScrollPixelsPerLine, 0 );
363
364 gtk_signal_emit_by_name( GTK_OBJECT(m_hAdjust), "value_changed" );
365 }
366
367 if ((y_pos != -1) && (m_yScrollPixelsPerLine))
368 {
369 int old_y = m_yScrollPosition;
370 m_yScrollPosition = y_pos;
371 m_vAdjust->value = y_pos;
372
373 m_targetWindow->ScrollWindow( 0, (old_y-m_yScrollPosition)*m_yScrollPixelsPerLine );
374
375 gtk_signal_emit_by_name( GTK_OBJECT(m_vAdjust), "value_changed" );
376 }
377 }
378
379 void wxScrolledWindow::GtkVScroll( float value )
380 {
381 if (!m_targetWindow)
382 return;
383
384 if (m_yScrollPixelsPerLine == 0)
385 return;
386
387 int y_pos = (int)(value+0.5);
388
389 if (y_pos == m_yScrollPosition)
390 return;
391
392 int old_y = m_yScrollPosition;
393 m_yScrollPosition = y_pos;
394
395 m_targetWindow->ScrollWindow( 0, (old_y-m_yScrollPosition)*m_yScrollPixelsPerLine );
396 }
397
398 void wxScrolledWindow::GtkHScroll( float value )
399 {
400 if (!m_targetWindow)
401 return;
402
403 if (m_xScrollPixelsPerLine == 0)
404 return;
405
406 int x_pos = (int)(value+0.5);
407
408 if (x_pos == m_xScrollPosition)
409 return;
410
411 int old_x = m_xScrollPosition;
412 m_xScrollPosition = x_pos;
413
414 m_targetWindow->ScrollWindow( (old_x-m_xScrollPosition)*m_xScrollPixelsPerLine, 0 );
415 }
416
417 void wxScrolledWindow::EnableScrolling (bool x_scroll, bool y_scroll)
418 {
419 m_xScrollingEnabled = x_scroll;
420 m_yScrollingEnabled = y_scroll;
421 }
422
423 void wxScrolledWindow::GetVirtualSize (int *x, int *y) const
424 {
425 if ( x )
426 *x = m_xScrollPixelsPerLine * m_xScrollLines;
427 if ( y )
428 *y = m_yScrollPixelsPerLine * m_yScrollLines;
429 }
430
431 // Where the current view starts from
432 void wxScrolledWindow::GetViewStart (int *x, int *y) const
433 {
434 if ( x )
435 *x = m_xScrollPosition;
436 if ( y )
437 *y = m_yScrollPosition;
438 }
439
440 void wxScrolledWindow::CalcScrolledPosition(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 void wxScrolledWindow::CalcUnscrolledPosition(int x, int y, int *xx, int *yy) const
449 {
450 if ( xx )
451 *xx = x + m_xScrollPosition * m_xScrollPixelsPerLine;
452 if ( yy )
453 *yy = y + m_yScrollPosition * m_yScrollPixelsPerLine;
454 }
455
456 // ----------------------------------------------------------------------------
457 // event handlers
458 // ----------------------------------------------------------------------------
459
460 // Default OnSize resets scrollbars, if any
461 void wxScrolledWindow::OnSize(wxSizeEvent& WXUNUSED(event))
462 {
463 #if wxUSE_CONSTRAINTS
464 if (GetAutoLayout())
465 Layout();
466 #endif
467
468 AdjustScrollbars();
469 }
470
471 // This calls OnDraw, having adjusted the origin according to the current
472 // scroll position
473 void wxScrolledWindow::OnPaint(wxPaintEvent& WXUNUSED(event))
474 {
475 wxPaintDC dc(this);
476 PrepareDC(dc);
477
478 OnDraw(dc);
479 }
480
481 // kbd handling: notice that we use OnChar() and not OnKeyDown() for
482 // compatibility here - if we used OnKeyDown(), the programs which process
483 // arrows themselves in their OnChar() would never get the message and like
484 // this they always have the priority
485 void wxScrolledWindow::OnChar(wxKeyEvent& event)
486 {
487 int stx, sty, // view origin
488 szx, szy, // view size (total)
489 clix, cliy; // view size (on screen)
490
491 ViewStart(&stx, &sty);
492 GetClientSize(&clix, &cliy);
493 GetVirtualSize(&szx, &szy);
494
495 if( m_xScrollPixelsPerLine )
496 {
497 clix /= m_xScrollPixelsPerLine;
498 szx /= m_xScrollPixelsPerLine;
499 }
500 else
501 {
502 clix = 0;
503 szx = -1;
504 }
505 if( m_yScrollPixelsPerLine )
506 {
507 cliy /= m_yScrollPixelsPerLine;
508 szy /= m_yScrollPixelsPerLine;
509 }
510 else
511 {
512 cliy = 0;
513 szy = -1;
514 }
515
516 int dsty;
517 switch ( event.KeyCode() )
518 {
519 case WXK_PAGEUP:
520 case WXK_PRIOR:
521 dsty = sty - (5 * cliy / 6);
522 Scroll(-1, (dsty == -1) ? 0 : dsty);
523 break;
524
525 case WXK_PAGEDOWN:
526 case WXK_NEXT:
527 Scroll(-1, sty + (5 * cliy / 6));
528 break;
529
530 case WXK_HOME:
531 Scroll(0, event.ControlDown() ? 0 : -1);
532 break;
533
534 case WXK_END:
535 Scroll(szx - clix, event.ControlDown() ? szy - cliy : -1);
536 break;
537
538 case WXK_UP:
539 Scroll(-1, sty - 1);
540 break;
541
542 case WXK_DOWN:
543 Scroll(-1, sty + 1);
544 break;
545
546 case WXK_LEFT:
547 Scroll(stx - 1, -1);
548 break;
549
550 case WXK_RIGHT:
551 Scroll(stx + 1, -1);
552 break;
553
554 default:
555 // not for us
556 event.Skip();
557 }
558 }