1 ///////////////////////////////////////////////////////////////////////////////
2 // Name:        src/gtk/activityindicator.cpp
3 // Purpose:     wxActivityIndicator implementation for wxGTK.
4 // Author:      Vadim Zeitlin
5 // Created:     2015-03-05
6 // Copyright:   (c) 2015 Vadim Zeitlin <vadim@wxwidgets.org>
7 // Licence:     wxWindows licence
8 ///////////////////////////////////////////////////////////////////////////////
9 
10 // ============================================================================
11 // declarations
12 // ============================================================================
13 
14 // ----------------------------------------------------------------------------
15 // headers
16 // ----------------------------------------------------------------------------
17 
18 // for compilers that support precompilation, includes "wx.h".
19 #include "wx/wxprec.h"
20 
21 
22 #if wxUSE_ACTIVITYINDICATOR && defined(__WXGTK220__)
23 
24 #include "wx/activityindicator.h"
25 
26 #include "wx/math.h"
27 
28 #include "wx/gtk/private/wrapgtk.h"
29 
30 // Macro return the specified expression only if GTK+ run time version is less
31 // than 2.20 and compiling it only if it is less than 3.0 (which is why this
32 // has to be a macro and not a function).
33 #if defined(__WXGTK220__) && !defined(__WXGTK3__)
34     #define RETURN_IF_NO_GTK_SPINNER(expr) \
35         if ( !wx_is_at_least_gtk2(20) ) { return expr; }
36 #else
37     #define RETURN_IF_NO_GTK_SPINNER(expr)
38 #endif
39 
40 // ============================================================================
41 // implementation
42 // ============================================================================
43 
44 wxIMPLEMENT_DYNAMIC_CLASS(wxActivityIndicator, wxControl);
45 
46 bool
Create(wxWindow * parent,wxWindowID winid,const wxPoint & pos,const wxSize & size,long style,const wxString & name)47 wxActivityIndicator::Create(wxWindow* parent,
48                             wxWindowID winid,
49                             const wxPoint& pos,
50                             const wxSize& size,
51                             long style,
52                             const wxString& name)
53 {
54     RETURN_IF_NO_GTK_SPINNER(
55         wxActivityIndicatorGeneric::Create(parent, winid, pos, size, style, name)
56     )
57 
58     if ( !PreCreation(parent, pos, size) ||
59              !CreateBase(parent, winid, pos, size, style, name) )
60         return false;
61 
62     m_widget = gtk_spinner_new();
63     g_object_ref(m_widget);
64 
65     m_parent->DoAddChild(this);
66 
67     PostCreation(size);
68 
69     return true;
70 }
71 
Start()72 void wxActivityIndicator::Start()
73 {
74     RETURN_IF_NO_GTK_SPINNER( wxActivityIndicatorGeneric::Start() )
75 
76     wxCHECK_RET( m_widget, wxS("Must be created first") );
77 
78     gtk_spinner_start(GTK_SPINNER(m_widget));
79 }
80 
Stop()81 void wxActivityIndicator::Stop()
82 {
83     RETURN_IF_NO_GTK_SPINNER( wxActivityIndicatorGeneric::Stop() )
84 
85     wxCHECK_RET( m_widget, wxS("Must be created first") );
86 
87     gtk_spinner_stop(GTK_SPINNER(m_widget));
88 }
89 
IsRunning() const90 bool wxActivityIndicator::IsRunning() const
91 {
92     RETURN_IF_NO_GTK_SPINNER( wxActivityIndicatorGeneric::IsRunning() )
93 
94     if ( !m_widget )
95         return false;
96 
97     gboolean b;
98     g_object_get(m_widget, "active", &b, NULL);
99 
100     return b != 0;
101 }
102 
DoGetBestClientSize() const103 wxSize wxActivityIndicator::DoGetBestClientSize() const
104 {
105     RETURN_IF_NO_GTK_SPINNER( wxActivityIndicatorGeneric::DoGetBestClientSize() )
106 
107     if ( !m_widget )
108         return wxDefaultSize;
109 
110     gint w, h;
111 
112 #ifdef __WXGTK3__
113     // gtk_widget_get_preferred_size() seems to return the size based on the
114     // current size of the widget and also always returns 0 if it is hidden,
115     // so ask GtkSpinner for its preferred size directly instead of using it.
116     GtkWidgetClass* const wc = GTK_WIDGET_GET_CLASS(m_widget);
117 
118     // We're not interested in the natural size (and it's the same as minimal
119     // one anyhow currently), but we still need a non-NULL pointer for it.
120     gint dummy;
121     wc->get_preferred_width(m_widget, &w, &dummy);
122     wc->get_preferred_height(m_widget, &h, &dummy);
123 #else // GTK+ 2
124     // GTK+ 2 doesn't return any valid preferred size for this control, so we
125     // use the size of something roughly equivalent to it.
126     gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &w, &h);
127 #endif // GTK+ 3/2
128 
129     // Adjust for the window variant: note that the default size in GTK+ 3 is
130     // really small (12px until ~3.10, 16px since then), so we use this size as
131     // the small size and double it for the normal size.
132     double factor = 0.;
133     switch ( GetWindowVariant() )
134     {
135         case wxWINDOW_VARIANT_MAX:
136             wxFAIL_MSG(wxS("Invalid window variant"));
137             wxFALLTHROUGH;
138 
139         case wxWINDOW_VARIANT_NORMAL:
140             factor = 2.;
141             break;
142 
143         case wxWINDOW_VARIANT_SMALL:
144             factor = 1.;
145             break;
146 
147         case wxWINDOW_VARIANT_MINI:
148             factor = 0.75;
149             break;
150 
151         case wxWINDOW_VARIANT_LARGE:
152             // GTK+ 3.11+ limits GtkSpinner size to twice its minimal size, so
153             // the effective factor here is actually just 2, i.e. the same as
154             // for the normal size, but use something larger just in case GTK+
155             // changes its mind again later.
156             factor = 2.5;
157             break;
158     }
159 
160     wxASSERT_MSG( !wxIsSameDouble(factor, 0), wxS("Unknown window variant") );
161 
162     return wxSize(wxRound(w*factor), wxRound(h*factor));
163 }
164 
165 #endif // wxUSE_ACTIVITYINDICATOR
166