1 /**************************************************************************
2 Copyright:
3 (C) 2008 - 2012 Alexander Shaduri <ashaduri 'at' gmail.com>
4 License: See LICENSE_gsmartcontrol.txt
5 ***************************************************************************/
6 /// \file
7 /// \author Alexander Shaduri
8 /// \ingroup applib
9 /// \weakgroup applib
10 /// @{
11
12 #ifndef APP_UI_RES_UTILS_H
13 #define APP_UI_RES_UTILS_H
14
15 #include <string>
16 #include <gtkmm.h>
17
18 #include "hz/hz_config.h" // feature test macros (ENABLE_*)
19
20 // Old glibmm versions had exceptions but didn't define this at all.
21 // New ones define it to 0 if there are no glibmm exceptions.
22 #if !defined GLIBMM_EXCEPTIONS_ENABLED || GLIBMM_EXCEPTIONS_ENABLED
23 #include <memory> // std::auto_ptr
24 #endif
25
26 #include "hz/debug.h"
27 #include "hz/instance_manager.h"
28 #include "hz/down_cast.h"
29 #include "hz/res_data.h"
30
31 #include "gui_utils.h" // gui_show_error_dialog
32
33
34
35 /// \def APP_UI_RES_DATA_INIT(res_name)
36 /// Use these in window class definitions to declare ui resources.
37 /// E.g. APP_UI_RES_DATA_INIT(main_window) will search for
38 /// main_window.ui in data file search paths.
39 /// Or, if you're using compiled-in buffers, it will make them available.
40 #define APP_UI_RES_DATA_INIT(res_name) \
41 HZ_RES_DATA_INIT_NAMED(res_name##_ui, #res_name ".ui", UIResDataBase); \
42 struct UIResData : public UIResDataBase { \
43 UIResData() \
44 { \
45 UIResDataBase::root_name = #res_name; /* we need original name here, not with _ui */ \
46 } \
47 }
48
49
50
51 /// \typedef app_ui_res_ref_t
52 /// A reference-counting pointer to application UI resource
53 typedef Glib::RefPtr<Gtk::Builder> app_ui_res_ref_t;
54
55
56 /// \fn bool app_ui_res_create_from(app_ui_res_ref_t& ref, const unsigned char* buf, unsigned int buf_size, std::string& error_msg)
57 /// Create application UI resource from a static buffer.
app_ui_res_create_from(app_ui_res_ref_t & ref,const unsigned char * buf,unsigned int buf_size,std::string & error_msg)58 inline bool app_ui_res_create_from(app_ui_res_ref_t& ref,
59 const unsigned char* buf, unsigned int buf_size, std::string& error_msg)
60 {
61 if (!buf_size || !buf || !buf[0]) {
62 error_msg = "Cannot load data buffers.";
63 return false;
64 }
65
66 #if !defined GLIBMM_EXCEPTIONS_ENABLED || GLIBMM_EXCEPTIONS_ENABLED
67 try {
68 // ref->add_from_file("main_window.ui");
69 ref->add_from_string(reinterpret_cast<const char*>(buf), static_cast<gsize>(buf_size));
70 }
71 catch (Gtk::BuilderError& ex) { // the docs say Glib::MarkupError, but examples say otherwise.
72 error_msg = ex.what();
73 return false;
74 }
75
76 #else
77
78 std::auto_ptr<Glib::BuilderError> error;
79 ref->add_from_string(reinterpret_cast<const char*>(buf), static_cast<gsize>(buf_size),"", "", error);
80
81 if (error.get()) {
82 error_msg = error->what();
83 return false;
84 }
85 #endif
86
87 return true;
88 }
89
90
91
92
93 // These allow easy attaching of gtkbuilder widget signals to member functions
94
95 /// Connect member function (callback) to signal \c signal_name on widget
96 /// \c ui_element, where \c ui_element is the widget's gtkbuilder name.
97 #define APP_UI_RES_CONNECT(ui_element, signal_name, callback) \
98 if (true) { \
99 if (!ui_element) \
100 this->lookup_object(#ui_element, ui_element); \
101 if (ui_element) { \
102 ui_element->signal_ ## signal_name ().connect(sigc::mem_fun(*this, &self_type::callback)); \
103 } \
104 } else (void)0
105
106
107 /// Connect member function (callback) with a name of \c on_<widget_name>_<signal_name>
108 /// to signal \c signal_name on widget \c ui_element, where \c ui_element is the
109 /// widget's gtkbuilder name.
110 #define APP_UI_RES_AUTO_CONNECT(ui_element, signal_name) \
111 APP_UI_RES_CONNECT(ui_element, signal_name, on_ ## ui_element ## _ ## signal_name)
112
113
114
115
116
117 /// Inherit this when using GtkBuilder-enabled windows (or any other GtkBuilder-enabled objects).
118 /// \c Child is the child class that inherits all the functionality of having instance lifetime
119 /// management and other benefits.
120 /// If \c MultiInstance is false, create() will return the same instance each time.
121 template<class Child, bool MultiInstance, class WidgetType = Gtk::Window>
122 class AppUIResWidget : public WidgetType, public hz::InstanceManager<Child, MultiInstance> {
123 public:
124
125 /// Instance class type, which is also the parent class.
126 typedef hz::InstanceManager<Child, MultiInstance> instance_class;
127 friend class Gtk::Builder; // allow construction through gtkbuilder
128 friend class hz::InstanceManager<Child, MultiInstance>; // allow construction through instance class
129
130
131 /// Override parent hz::InstanceManager's function because of non-trivial constructor
create()132 static Child* create()
133 {
134 if (hz::InstanceManager<Child, MultiInstance>::has_single_instance()) // for single-instance objects
135 return hz::InstanceManager<Child, MultiInstance>::get_single_instance();
136
137 std::string error;
138 app_ui_res_ref_t ui = Gtk::Builder::create();
139 const typename Child::UIResData data; // this holds the GtkBuilder data
140
141 // this does the actual object construction
142 bool success = app_ui_res_create_from(ui, data.buf, data.size, error);
143
144 if (!success) {
145 std::string msg = "Fatal error: Cannot create UI-resource widgets: " + error;
146 debug_out_fatal("app", msg << "\n");
147 gui_show_error_dialog(msg);
148 return 0;
149 }
150
151 Child* o = 0;
152 ui->get_widget_derived(data.root_name, o);
153
154 if (!o) {
155 std::string msg = "Fatal error: Cannot get root widget from UI-resource-created hierarchy.";
156 debug_out_fatal("app", msg << "\n");
157 gui_show_error_dialog(msg);
158 return 0;
159 }
160
161 o->obj_create();
162
163 hz::InstanceManager<Child, MultiInstance>::set_single_instance(o); // for single-instance objects
164
165 return o;
166 }
167
168
169 /// Get UI resource
get_ui()170 app_ui_res_ref_t get_ui()
171 {
172 return ref_ui_;
173 }
174
175
176 /// Find a widget in UI and return it.
lookup_widget(const Glib::ustring & name)177 Gtk::Widget* lookup_widget(const Glib::ustring& name)
178 {
179 return lookup_widget<Gtk::Widget*>(name);
180 }
181
182
183 /// Find a widget in UI and return it.
184 template<typename WidgetPtr>
lookup_widget(const Glib::ustring & name)185 WidgetPtr lookup_widget(const Glib::ustring& name)
186 {
187 WidgetPtr w = 0;
188 return lookup_widget(name, w);
189 }
190
191
192 /// Find a widget in UI and return it.
193 template<typename WidgetPtr>
lookup_widget(const Glib::ustring & name,WidgetPtr & w)194 WidgetPtr lookup_widget(const Glib::ustring& name, WidgetPtr& w)
195 {
196 ref_ui_->get_widget(name, w);
197 return w;
198 }
199
200
201 /// Find an object in UI and return it.
lookup_object(const Glib::ustring & name)202 Glib::Object* lookup_object(const Glib::ustring& name)
203 {
204 return ref_ui_->get_object(name).operator->(); // silly RefPtr doesn't have get().
205 }
206
207
208 /// Find an object in UI and return it.
209 template<typename ObjectPtr>
lookup_object(const Glib::ustring & name)210 ObjectPtr lookup_object(const Glib::ustring& name)
211 {
212 ObjectPtr obj = 0;
213 return lookup_object(name, obj);
214 }
215
216
217 /// Find an object in UI and return it.
218 template<typename ObjectPtr>
lookup_object(const Glib::ustring & name,ObjectPtr & obj)219 ObjectPtr lookup_object(const Glib::ustring& name, ObjectPtr& obj)
220 {
221 return (obj = hz::down_cast<ObjectPtr>(lookup_object(name))); // up, then down
222 }
223
224
225
226 protected:
227
228 typedef Child self_type; ///< This is needed by APP_UI_ macros
229 typedef WidgetType widget_type; ///< This is needed by APP_UI_ macros
230
231
232 // protected constructor / destructor, use create() / destroy() instead of new / delete.
233
234 /// GtkBuilder needs this constructor in a child.
235 /// BaseObjectType is a C type, defined in specific Gtk:: widget class.
AppUIResWidget(typename WidgetType::BaseObjectType * gtkcobj,const app_ui_res_ref_t & ref_ui)236 AppUIResWidget(typename WidgetType::BaseObjectType* gtkcobj, const app_ui_res_ref_t& ref_ui)
237 : WidgetType(gtkcobj), ref_ui_(ref_ui)
238 {
239 // manually connecting signals:
240 // this->signal_delete_event().connect(sigc::mem_fun(*this, &MainWindow::on_main_window_delete));
241
242 // signals of GtkBuilder-created objects:
243 // Gtk::ToolButton* rescan_devices_toolbutton = 0;
244 // APP_UI_RES_AUTO_CONNECT(rescan_devices_toolbutton, clicked);
245
246 // show();
247 }
248
249
250 /// Virtual destructor
~AppUIResWidget()251 virtual ~AppUIResWidget()
252 { }
253
254
255 private:
256
257 app_ui_res_ref_t ref_ui_; ///< UI resource
258
259 };
260
261
262
263
264
265
266 #endif
267
268 /// @}
269