1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk/scrolwin.cpp
3 // Purpose: wxScrolledWindow implementation
4 // Author: Robert Roebling
5 // Modified by: Ron Lee
6 // Vadim Zeitlin: removed 90% of duplicated common code
7 // Created: 01/02/97
8 // Copyright: (c) Robert Roebling
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // For compilers that support precompilation, includes "wx.h".
13 #include "wx/wxprec.h"
14
15 #ifdef __BORLANDC__
16 #pragma hdrstop
17 #endif
18
19 #include "wx/scrolwin.h"
20
21 #include <gtk/gtk.h>
22 #include "wx/gtk/private/gtk2-compat.h"
23
24 // ----------------------------------------------------------------------------
25 // wxScrollHelper implementation
26 // ----------------------------------------------------------------------------
27
SetScrollbars(int pixelsPerUnitX,int pixelsPerUnitY,int noUnitsX,int noUnitsY,int xPos,int yPos,bool noRefresh)28 void wxScrollHelper::SetScrollbars(int pixelsPerUnitX, int pixelsPerUnitY,
29 int noUnitsX, int noUnitsY,
30 int xPos, int yPos,
31 bool noRefresh)
32 {
33 // prevent programmatic position changes from causing scroll events
34 m_win->SetScrollPos(wxHORIZONTAL, xPos);
35 m_win->SetScrollPos(wxVERTICAL, yPos);
36
37 base_type::SetScrollbars(
38 pixelsPerUnitX, pixelsPerUnitY, noUnitsX, noUnitsY, xPos, yPos, noRefresh);
39 }
40
DoAdjustScrollbar(GtkRange * range,int pixelsPerLine,int winSize,int virtSize,int * pos,int * lines,int * linesPerPage)41 void wxScrollHelper::DoAdjustScrollbar(GtkRange* range,
42 int pixelsPerLine,
43 int winSize,
44 int virtSize,
45 int *pos,
46 int *lines,
47 int *linesPerPage)
48 {
49 if (!range)
50 return;
51
52 int upper;
53 int page_size;
54 if (pixelsPerLine > 0 && winSize > 0 && winSize < virtSize)
55 {
56 upper = (virtSize + pixelsPerLine - 1) / pixelsPerLine;
57 page_size = winSize / pixelsPerLine;
58 if (page_size == 0)
59 page_size = 1;
60 *lines = upper;
61 *linesPerPage = page_size;
62 }
63 else
64 {
65 // GtkRange won't allow upper == lower, so for disabled state use [0,1]
66 // with a page size of 1. This will also clamp position to 0.
67 upper = 1;
68 page_size = 1;
69 *lines = 0;
70 *linesPerPage = 0;
71 }
72
73 GtkAdjustment* adj = gtk_range_get_adjustment(range);
74 const double adj_upper = gtk_adjustment_get_upper(adj);
75 const double adj_page_size = gtk_adjustment_get_page_size(adj);
76 if (adj_upper != upper || adj_page_size != page_size)
77 {
78 const bool wasVisible = adj_upper > adj_page_size;
79
80 g_object_freeze_notify(G_OBJECT(adj));
81 gtk_range_set_increments(range, 1, page_size);
82 gtk_adjustment_set_page_size(adj, page_size);
83 gtk_range_set_range(range, 0, upper);
84 g_object_thaw_notify(G_OBJECT(adj));
85
86 const bool isVisible = gtk_adjustment_get_upper(adj) > gtk_adjustment_get_page_size(adj);
87 if (isVisible != wasVisible)
88 m_win->m_useCachedClientSize = false;
89 }
90
91 // ensure that the scroll position is always in valid range
92 if (*pos > *lines)
93 *pos = *lines;
94 }
95
AdjustScrollbars()96 void wxScrollHelper::AdjustScrollbars()
97 {
98 int vw, vh;
99 m_targetWindow->GetVirtualSize(&vw, &vh);
100 #ifdef __WXGTK3__
101 if (m_targetWindow != m_win)
102 {
103 // setting wxPizza preferred size keeps GtkScrolledWindow from causing
104 // an infinite sizing loop
105 gtk_widget_set_size_request(m_win->m_wxwindow, vw, vh);
106 }
107 #endif
108
109 int w, h;
110 const wxSize availSize = GetSizeAvailableForScrollTarget(
111 m_win->GetSize() - m_win->GetWindowBorderSize());
112 if ( availSize.x >= vw && availSize.y >= vh )
113 {
114 w = availSize.x;
115 h = availSize.y;
116
117 // we know that the scrollbars will be removed
118 DoAdjustHScrollbar(w, vw);
119 DoAdjustVScrollbar(h, vh);
120
121 return;
122 }
123
124 m_targetWindow->GetClientSize(&w, NULL);
125 DoAdjustHScrollbar(w, vw);
126
127 m_targetWindow->GetClientSize(NULL, &h);
128 DoAdjustVScrollbar(h, vh);
129
130 const int w_old = w;
131 m_targetWindow->GetClientSize(&w, NULL);
132 if ( w != w_old )
133 {
134 // It is necessary to repeat the calculations in this case to avoid an
135 // observed infinite series of size events, involving alternating
136 // changes in visibility of the scrollbars.
137 // At this point, GTK+ has already queued a resize, which will cause
138 // AdjustScrollbars() to be called again. If the scrollbar visibility
139 // is not correct before then, yet another resize will occur, possibly
140 // leading to an unending series if the sizes are just right.
141 DoAdjustHScrollbar(w, vw);
142
143 m_targetWindow->GetClientSize(NULL, &h);
144 DoAdjustVScrollbar(h, vh);
145 }
146 }
147
DoScrollOneDir(int orient,int pos,int pixelsPerLine,int * posOld)148 void wxScrollHelper::DoScrollOneDir(int orient,
149 int pos,
150 int pixelsPerLine,
151 int *posOld)
152 {
153 if ( pos != -1 && pos != *posOld && pixelsPerLine )
154 {
155 m_win->SetScrollPos(orient, pos);
156 pos = m_win->GetScrollPos(orient);
157
158 int diff = (*posOld - pos)*pixelsPerLine;
159 m_targetWindow->ScrollWindow(orient == wxHORIZONTAL ? diff : 0,
160 orient == wxHORIZONTAL ? 0 : diff);
161
162 *posOld = pos;
163 }
164 }
165
DoScroll(int x_pos,int y_pos)166 void wxScrollHelper::DoScroll( int x_pos, int y_pos )
167 {
168 wxCHECK_RET( m_targetWindow != 0, wxT("No target window") );
169
170 DoScrollOneDir(wxHORIZONTAL, x_pos, m_xScrollPixelsPerLine, &m_xScrollPosition);
171 DoScrollOneDir(wxVERTICAL, y_pos, m_yScrollPixelsPerLine, &m_yScrollPosition);
172 }
173
174 // ----------------------------------------------------------------------------
175 // scrollbars visibility
176 // ----------------------------------------------------------------------------
177
178 namespace
179 {
180
GtkPolicyFromWX(wxScrollbarVisibility visibility)181 GtkPolicyType GtkPolicyFromWX(wxScrollbarVisibility visibility)
182 {
183 GtkPolicyType policy;
184 switch ( visibility )
185 {
186 case wxSHOW_SB_NEVER:
187 policy = GTK_POLICY_NEVER;
188 break;
189
190 case wxSHOW_SB_DEFAULT:
191 policy = GTK_POLICY_AUTOMATIC;
192 break;
193
194 default:
195 wxFAIL_MSG( wxS("unknown scrollbar visibility") );
196 // fall through
197
198 case wxSHOW_SB_ALWAYS:
199 policy = GTK_POLICY_ALWAYS;
200 break;
201 }
202
203 return policy;
204 }
205
206 } // anonymous namespace
207
IsScrollbarShown(int orient) const208 bool wxScrollHelper::IsScrollbarShown(int orient) const
209 {
210 GtkScrolledWindow * const scrolled = GTK_SCROLLED_WINDOW(m_win->m_widget);
211 if ( !scrolled )
212 {
213 // By default, all windows are scrollable.
214 return true;
215 }
216
217 GtkPolicyType hpolicy, vpolicy;
218 gtk_scrolled_window_get_policy(scrolled, &hpolicy, &vpolicy);
219
220 GtkPolicyType policy = orient == wxHORIZONTAL ? hpolicy : vpolicy;
221
222 return policy != GTK_POLICY_NEVER;
223 }
224
DoShowScrollbars(wxScrollbarVisibility horz,wxScrollbarVisibility vert)225 void wxScrollHelper::DoShowScrollbars(wxScrollbarVisibility horz,
226 wxScrollbarVisibility vert)
227 {
228 GtkScrolledWindow * const scrolled = GTK_SCROLLED_WINDOW(m_win->m_widget);
229 wxCHECK_RET( scrolled, "window must be created" );
230
231 gtk_scrolled_window_set_policy(scrolled,
232 GtkPolicyFromWX(horz),
233 GtkPolicyFromWX(vert));
234 }
235