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