1 /* SPDX-FileCopyrightText: 2017 - Sébastien Wilmet <swilmet@gnome.org>
2  * SPDX-License-Identifier: LGPL-3.0-or-later
3  */
4 
5 #include "tepl-abstract-factory.h"
6 #include "tepl-tab-label.h"
7 
8 /**
9  * SECTION:abstract-factory
10  * @Short_description: Abstract factory singleton class
11  * @Title: TeplAbstractFactory
12  *
13  * The Tepl framework uses the #TeplAbstractFactory singleton to create some
14  * objects and widgets. By creating a subclass of #TeplAbstractFactory (to
15  * override the desired virtual functions) and setting the instance with
16  * tepl_abstract_factory_set_singleton(), an application can tell Tepl to create
17  * custom objects and widgets.
18  *
19  * Note that #GtkTextViewClass has the ::create_buffer factory method, that
20  * #TeplView overrides to create a #TeplBuffer. How the #TeplView and
21  * #TeplBuffer are created can be customized with the ::create_tab vfunc of
22  * #TeplAbstractFactory.
23  *
24  * Recommendation for the subclass name: in Tepl, #TeplAbstractFactory is an
25  * abstract class, but in an application it is a concrete class. So
26  * “MyappAbstractFactory” is not a good name for a #TeplAbstractFactory
27  * subclass. “MyappFactory” is a better name (of course change “Myapp” with the
28  * application namespace).
29  */
30 
31 /* API design:
32  *
33  * An example of another sub-classable singleton is PeasEngine, in libpeas. I
34  * didn't really like the PeasEngine implementation with get_default(), so I've
35  * implemented TeplAbstractFactory differently.
36  * tepl_abstract_factory_set_singleton() is more explicit. And doing things
37  * explicitly is clearer. -- swilmet
38  */
39 
40 static TeplAbstractFactory *singleton = NULL;
41 
G_DEFINE_TYPE(TeplAbstractFactory,tepl_abstract_factory,G_TYPE_OBJECT)42 G_DEFINE_TYPE (TeplAbstractFactory, tepl_abstract_factory, G_TYPE_OBJECT)
43 
44 static void
45 tepl_abstract_factory_finalize (GObject *object)
46 {
47 	if (singleton == TEPL_ABSTRACT_FACTORY (object))
48 	{
49 		singleton = NULL;
50 	}
51 
52 	G_OBJECT_CLASS (tepl_abstract_factory_parent_class)->finalize (object);
53 }
54 
55 static TeplTab *
tepl_abstract_factory_create_tab_default(TeplAbstractFactory * factory)56 tepl_abstract_factory_create_tab_default (TeplAbstractFactory *factory)
57 {
58 	return tepl_tab_new ();
59 }
60 
61 static GtkWidget *
tepl_abstract_factory_create_tab_label_default(TeplAbstractFactory * factory,TeplTab * tab)62 tepl_abstract_factory_create_tab_label_default (TeplAbstractFactory *factory,
63 						TeplTab             *tab)
64 {
65 	return tepl_tab_label_new (tab);
66 }
67 
68 static TeplFile *
tepl_abstract_factory_create_file_default(TeplAbstractFactory * factory)69 tepl_abstract_factory_create_file_default (TeplAbstractFactory *factory)
70 {
71 	return tepl_file_new ();
72 }
73 
74 static void
tepl_abstract_factory_class_init(TeplAbstractFactoryClass * klass)75 tepl_abstract_factory_class_init (TeplAbstractFactoryClass *klass)
76 {
77 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
78 
79 	object_class->finalize = tepl_abstract_factory_finalize;
80 
81 	klass->create_tab = tepl_abstract_factory_create_tab_default;
82 	klass->create_tab_label = tepl_abstract_factory_create_tab_label_default;
83 	klass->create_file = tepl_abstract_factory_create_file_default;
84 }
85 
86 static void
tepl_abstract_factory_init(TeplAbstractFactory * factory)87 tepl_abstract_factory_init (TeplAbstractFactory *factory)
88 {
89 }
90 
91 /**
92  * tepl_abstract_factory_set_singleton:
93  * @factory: (transfer full): a #TeplAbstractFactory.
94  *
95  * Sets the #TeplAbstractFactory singleton. This should be called early in
96  * main(), for example just after calling tepl_init().
97  *
98  * This function must be called only once, before the first call to
99  * tepl_abstract_factory_get_singleton().
100  *
101  * Tepl takes ownership of the @factory reference.
102  *
103  * Since: 3.0
104  */
105 void
tepl_abstract_factory_set_singleton(TeplAbstractFactory * factory)106 tepl_abstract_factory_set_singleton (TeplAbstractFactory *factory)
107 {
108 	g_return_if_fail (TEPL_IS_ABSTRACT_FACTORY (factory));
109 
110 	if (singleton != NULL)
111 	{
112 		g_warning ("%s(): the TeplAbstractFactory singleton is already created.",
113 			   G_STRFUNC);
114 		return;
115 	}
116 
117 	singleton = factory;
118 }
119 
120 /**
121  * tepl_abstract_factory_get_singleton:
122  *
123  * Gets the #TeplAbstractFactory singleton instance.
124  *
125  * If tepl_abstract_factory_set_singleton() has not been called, the singleton
126  * is created with a #TeplAbstractFactory instance.
127  *
128  * Returns: (transfer none): the #TeplAbstractFactory singleton instance.
129  * Since: 3.0
130  */
131 TeplAbstractFactory *
tepl_abstract_factory_get_singleton(void)132 tepl_abstract_factory_get_singleton (void)
133 {
134 	if (G_UNLIKELY (singleton == NULL))
135 	{
136 		singleton = g_object_new (TEPL_TYPE_ABSTRACT_FACTORY, NULL);
137 	}
138 
139 	return singleton;
140 }
141 
142 void
_tepl_abstract_factory_unref_singleton(void)143 _tepl_abstract_factory_unref_singleton (void)
144 {
145 	if (singleton != NULL)
146 	{
147 		g_object_unref (singleton);
148 	}
149 
150 	/* singleton is not set to NULL here, it is set to NULL in
151 	 * tepl_abstract_factory_finalize() (i.e. when we are sure that the ref
152 	 * count reaches 0).
153 	 */
154 }
155 
156 /**
157  * tepl_abstract_factory_create_main_window:
158  * @factory: the #TeplAbstractFactory.
159  * @app: a #GtkApplication.
160  *
161  * Creates a main #GtkApplicationWindow in the sense of
162  * tepl_application_window_is_main_window().
163  *
164  * Returns: (transfer floating) (nullable): a new main application window, or
165  * %NULL if the vfunc is not implemented.
166  */
167 GtkApplicationWindow *
tepl_abstract_factory_create_main_window(TeplAbstractFactory * factory,GtkApplication * app)168 tepl_abstract_factory_create_main_window (TeplAbstractFactory *factory,
169 					  GtkApplication      *app)
170 {
171 	g_return_val_if_fail (TEPL_IS_ABSTRACT_FACTORY (factory), NULL);
172 	g_return_val_if_fail (GTK_IS_APPLICATION (app), NULL);
173 
174 	if (TEPL_ABSTRACT_FACTORY_GET_CLASS (factory)->create_main_window != NULL)
175 	{
176 		return TEPL_ABSTRACT_FACTORY_GET_CLASS (factory)->create_main_window (factory, app);
177 	}
178 
179 	g_warning ("The TeplAbstractFactory::create_main_window vfunc is not implemented.");
180 	return NULL;
181 }
182 
183 /**
184  * tepl_abstract_factory_create_tab:
185  * @factory: the #TeplAbstractFactory.
186  *
187  * Returns: (transfer floating): a new #TeplTab.
188  * Since: 3.0
189  */
190 TeplTab *
tepl_abstract_factory_create_tab(TeplAbstractFactory * factory)191 tepl_abstract_factory_create_tab (TeplAbstractFactory *factory)
192 {
193 	g_return_val_if_fail (TEPL_IS_ABSTRACT_FACTORY (factory), NULL);
194 
195 	return TEPL_ABSTRACT_FACTORY_GET_CLASS (factory)->create_tab (factory);
196 }
197 
198 /**
199  * tepl_abstract_factory_create_tab_label:
200  * @factory: the #TeplAbstractFactory.
201  * @tab: a #TeplTab.
202  *
203  * Creates a new tab label for @tab, suitable for gtk_notebook_set_tab_label().
204  *
205  * Returns: (transfer floating) (nullable): a new #GtkWidget, or %NULL for the
206  * default tab label (“page N” with #GtkNotebook).
207  * Since: 3.0
208  */
209 GtkWidget *
tepl_abstract_factory_create_tab_label(TeplAbstractFactory * factory,TeplTab * tab)210 tepl_abstract_factory_create_tab_label (TeplAbstractFactory *factory,
211 					TeplTab             *tab)
212 {
213 	g_return_val_if_fail (TEPL_IS_ABSTRACT_FACTORY (factory), NULL);
214 	g_return_val_if_fail (TEPL_IS_TAB (tab), NULL);
215 
216 	return TEPL_ABSTRACT_FACTORY_GET_CLASS (factory)->create_tab_label (factory, tab);
217 }
218 
219 /**
220  * tepl_abstract_factory_create_file:
221  * @factory: the #TeplAbstractFactory.
222  *
223  * Returns: (transfer full): a new #TeplFile.
224  * Since: 4.0
225  */
226 TeplFile *
tepl_abstract_factory_create_file(TeplAbstractFactory * factory)227 tepl_abstract_factory_create_file (TeplAbstractFactory *factory)
228 {
229 	g_return_val_if_fail (TEPL_IS_ABSTRACT_FACTORY (factory), NULL);
230 
231 	return TEPL_ABSTRACT_FACTORY_GET_CLASS (factory)->create_file (factory);
232 }
233 
234 /**
235  * tepl_abstract_factory_create_metadata_manager_file:
236  * @factory: the #TeplAbstractFactory.
237  *
238  * Creates a new #GFile that is then intended to be used as an argument to
239  * tepl_metadata_manager_load_from_disk() and
240  * tepl_metadata_manager_save_to_disk(). This function just creates the #GFile
241  * object, it doesn't call any #TeplMetadataManager function.
242  *
243  * Returns: (transfer full) (nullable): a new #GFile, or %NULL if the vfunc is
244  * not implemented.
245  * Since: 5.0
246  */
247 GFile *
tepl_abstract_factory_create_metadata_manager_file(TeplAbstractFactory * factory)248 tepl_abstract_factory_create_metadata_manager_file (TeplAbstractFactory *factory)
249 {
250 	g_return_val_if_fail (TEPL_IS_ABSTRACT_FACTORY (factory), NULL);
251 
252 	if (TEPL_ABSTRACT_FACTORY_GET_CLASS (factory)->create_metadata_manager_file != NULL)
253 	{
254 		return TEPL_ABSTRACT_FACTORY_GET_CLASS (factory)->create_metadata_manager_file (factory);
255 	}
256 
257 	g_warning ("The TeplAbstractFactory::create_metadata_manager_file vfunc is not implemented.");
258 	return NULL;
259 }
260