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