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