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