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