1 /* gtd-provider.c
2 *
3 * Copyright (C) 2015-2020 Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #define G_LOG_DOMAIN "GtdProvider"
20
21 #include "gtd-provider.h"
22 #include "gtd-task.h"
23 #include "gtd-task-list.h"
24 #include "gtd-utils.h"
25
26 /**
27 * SECTION:gtd-provider
28 * @short_description:data sources for GNOME To do
29 * @title: GtdProvider
30 * @stability:Unstable
31 *
32 * The #GtdProvider is the interface that GNOME To Do uses to
33 * connect to data sources. It must provide ways to create, update
34 * and remove tasks and tasklists.
35 *
36 * A provider implementation must also expose which is the default
37 * tasklist among the tasklists it manages.
38 */
39
40 G_DEFINE_INTERFACE (GtdProvider, gtd_provider, GTD_TYPE_OBJECT)
41
42 enum
43 {
44 LIST_ADDED,
45 LIST_CHANGED,
46 LIST_REMOVED,
47 NUM_SIGNALS
48 };
49
50 static guint signals[NUM_SIGNALS] = { 0, };
51
52
53 static void
gtd_provider_default_init(GtdProviderInterface * iface)54 gtd_provider_default_init (GtdProviderInterface *iface)
55 {
56 /**
57 * GtdProvider::enabled:
58 *
59 * Whether the #GtdProvider is enabled.
60 */
61 g_object_interface_install_property (iface,
62 g_param_spec_boolean ("enabled",
63 "Identifier of the provider",
64 "The identifier of the provider",
65 FALSE,
66 G_PARAM_READABLE));
67
68 /**
69 * GtdProvider::icon:
70 *
71 * The icon of the #GtdProvider, e.g. the account icon
72 * of a GNOME Online Accounts' account.
73 */
74 g_object_interface_install_property (iface,
75 g_param_spec_object ("icon",
76 "Icon of the provider",
77 "The icon of the provider",
78 G_TYPE_ICON,
79 G_PARAM_READABLE));
80
81 /**
82 * GtdProvider::id:
83 *
84 * The unique identifier of the #GtdProvider.
85 */
86 g_object_interface_install_property (iface,
87 g_param_spec_string ("id",
88 "Identifier of the provider",
89 "The identifier of the provider",
90 NULL,
91 G_PARAM_READABLE));
92
93 /**
94 * GtdProvider::name:
95 *
96 * The user-visible name of the #GtdProvider.
97 */
98 g_object_interface_install_property (iface,
99 g_param_spec_string ("name",
100 "Name of the provider",
101 "The user-visible name of the provider",
102 NULL,
103 G_PARAM_READABLE));
104
105 /**
106 * GtdProvider::provider-type:
107 *
108 * The type of the #GtdProvider.
109 */
110 g_object_interface_install_property (iface,
111 g_param_spec_string ("provider-type",
112 "Type of the provider",
113 "The type of the provider",
114 NULL,
115 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
116
117 /**
118 * GtdProvider::description:
119 *
120 * The description of the #GtdProvider, e.g. the account user
121 * of a GNOME Online Accounts' account.
122 */
123 g_object_interface_install_property (iface,
124 g_param_spec_string ("description",
125 "Description of the provider",
126 "The description of the provider",
127 NULL,
128 G_PARAM_READABLE));
129
130 /**
131 * GtdProvider::list-added:
132 * @provider: a #GtdProvider
133 * @list: a #GtdTaskList
134 *
135 * The ::list-added signal is emmited after a #GtdTaskList
136 * is connected.
137 */
138 signals[LIST_ADDED] = g_signal_new ("list-added",
139 GTD_TYPE_PROVIDER,
140 G_SIGNAL_RUN_LAST,
141 0,
142 NULL,
143 NULL,
144 NULL,
145 G_TYPE_NONE,
146 1,
147 GTD_TYPE_TASK_LIST);
148
149 /**
150 * GtdProvider::list-changed:
151 * @provider: a #GtdProvider
152 * @list: a #GtdTaskList
153 *
154 * The ::list-changed signal is emmited after a #GtdTaskList
155 * has any of it's properties changed.
156 */
157 signals[LIST_CHANGED] = g_signal_new ("list-changed",
158 GTD_TYPE_PROVIDER,
159 G_SIGNAL_RUN_LAST,
160 0,
161 NULL,
162 NULL,
163 NULL,
164 G_TYPE_NONE,
165 1,
166 GTD_TYPE_TASK_LIST);
167
168 /**
169 * GtdProvider::list-removed:
170 * @provider: a #GtdProvider
171 * @list: a #GtdTaskList
172 *
173 * The ::list-removed signal is emmited after a #GtdTaskList
174 * is disconnected.
175 */
176 signals[LIST_REMOVED] = g_signal_new ("list-removed",
177 GTD_TYPE_PROVIDER,
178 G_SIGNAL_RUN_LAST,
179 0,
180 NULL,
181 NULL,
182 NULL,
183 G_TYPE_NONE,
184 1,
185 GTD_TYPE_TASK_LIST);
186 }
187
188 /**
189 * gtd_provider_get_id:
190 * @provider: a #GtdProvider
191 *
192 * Retrieves the identifier of @provider.
193 *
194 * Returns: (transfer none): the id of @provider
195 */
196 const gchar*
gtd_provider_get_id(GtdProvider * provider)197 gtd_provider_get_id (GtdProvider *provider)
198 {
199 g_return_val_if_fail (GTD_IS_PROVIDER (provider), NULL);
200 g_return_val_if_fail (GTD_PROVIDER_GET_IFACE (provider)->get_id, NULL);
201
202 return GTD_PROVIDER_GET_IFACE (provider)->get_id (provider);
203 }
204
205 /**
206 * gtd_provider_get_name:
207 * @provider: a #GtdProvider
208 *
209 * Retrieves the user-visible name of @provider.
210 *
211 * Returns: (transfer none): the name of @provider
212 */
213 const gchar*
gtd_provider_get_name(GtdProvider * provider)214 gtd_provider_get_name (GtdProvider *provider)
215 {
216 g_return_val_if_fail (GTD_IS_PROVIDER (provider), NULL);
217 g_return_val_if_fail (GTD_PROVIDER_GET_IFACE (provider)->get_name, NULL);
218
219 return GTD_PROVIDER_GET_IFACE (provider)->get_name (provider);
220 }
221
222 /**
223 * gtd_provider_get_provider_type:
224 * @provider: a #GtdProvider
225 *
226 * Retrieves the type of the @provider. This should return the
227 * same value, regardless of the account name.
228 *
229 * For example: "todoist", "todo-txt" or "google"
230 *
231 * Returns: (transfer none): the type of the @provider
232 */
233 const gchar*
gtd_provider_get_provider_type(GtdProvider * provider)234 gtd_provider_get_provider_type (GtdProvider *provider)
235 {
236 g_return_val_if_fail (GTD_IS_PROVIDER (provider), NULL);
237 g_return_val_if_fail (GTD_PROVIDER_GET_IFACE (provider)->get_name, NULL);
238
239 return GTD_PROVIDER_GET_IFACE (provider)->get_provider_type (provider);
240 }
241
242 /**
243 * gtd_provider_get_description:
244 * @provider: a #GtdProvider
245 *
246 * Retrieves the description of @provider.
247 *
248 * Returns: (transfer none): the description of @provider
249 */
250 const gchar*
gtd_provider_get_description(GtdProvider * provider)251 gtd_provider_get_description (GtdProvider *provider)
252 {
253 g_return_val_if_fail (GTD_IS_PROVIDER (provider), NULL);
254 g_return_val_if_fail (GTD_PROVIDER_GET_IFACE (provider)->get_description, NULL);
255
256 return GTD_PROVIDER_GET_IFACE (provider)->get_description (provider);
257 }
258
259 /**
260 * gtd_provider_get_enabled:
261 * @provider: a #GtdProvider
262 *
263 * Retrieves whether @provider is enabled or not. A disabled
264 * provider cannot be selected to be default nor be selected
265 * to add tasks to it.
266 *
267 * Returns: %TRUE if provider is enabled, %FALSE otherwise.
268 */
269 gboolean
gtd_provider_get_enabled(GtdProvider * provider)270 gtd_provider_get_enabled (GtdProvider *provider)
271 {
272 g_return_val_if_fail (GTD_IS_PROVIDER (provider), FALSE);
273 g_return_val_if_fail (GTD_PROVIDER_GET_IFACE (provider)->get_enabled, FALSE);
274
275 return GTD_PROVIDER_GET_IFACE (provider)->get_enabled (provider);
276 }
277
278 /**
279 * gtd_provider_refresh:
280 * @provider: a #GtdProvider
281 *
282 * Asks the provider to refresh. Online providers may want to
283 * synchronize tasks and tasklists, credentials, etc, when this
284 * is called.
285 *
286 * This is an optional feature. Providers that do not implement
287 * the "refresh" vfunc will be ignored.
288 */
289 void
gtd_provider_refresh(GtdProvider * provider)290 gtd_provider_refresh (GtdProvider *provider)
291 {
292 g_return_if_fail (GTD_IS_PROVIDER (provider));
293
294 if (GTD_PROVIDER_GET_IFACE (provider)->refresh)
295 GTD_PROVIDER_GET_IFACE (provider)->refresh (provider);
296 }
297
298 /**
299 * gtd_provider_get_icon:
300 * @provider: a #GtdProvider
301 *
302 * The icon of @provider.
303 *
304 * Returns: (transfer none): a #GIcon
305 */
306 GIcon*
gtd_provider_get_icon(GtdProvider * provider)307 gtd_provider_get_icon (GtdProvider *provider)
308 {
309 g_return_val_if_fail (GTD_IS_PROVIDER (provider), NULL);
310 g_return_val_if_fail (GTD_PROVIDER_GET_IFACE (provider)->get_icon, NULL);
311
312 return GTD_PROVIDER_GET_IFACE (provider)->get_icon (provider);
313 }
314
315 /**
316 * gtd_provider_create_task:
317 * @provider: a #GtdProvider
318 * @task: a #GtdTask
319 * @due_date: (nullable): a #GDateTime
320 * @cancellable: (nullable): a #GCancellable
321 * @callback: (scope async): a callback
322 * @user_data: (closure): user data for @callback
323 *
324 * Creates the given task in @provider.
325 */
326 void
gtd_provider_create_task(GtdProvider * provider,GtdTaskList * list,const gchar * title,GDateTime * due_date,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)327 gtd_provider_create_task (GtdProvider *provider,
328 GtdTaskList *list,
329 const gchar *title,
330 GDateTime *due_date,
331 GCancellable *cancellable,
332 GAsyncReadyCallback callback,
333 gpointer user_data)
334 {
335 g_return_if_fail (GTD_IS_PROVIDER (provider));
336 g_return_if_fail (GTD_PROVIDER_GET_IFACE (provider)->create_task);
337
338 GTD_PROVIDER_GET_IFACE (provider)->create_task (provider,
339 list,
340 title,
341 due_date,
342 cancellable,
343 callback,
344 user_data);
345 }
346
347 /**
348 * gtd_provider_create_task_finish:
349 * @self: a #GtdProvider
350 * @result: a #GAsyncResult
351 * @error: (direction out)(nullable): return location for a #GError
352 *
353 * Finishes creating the task.
354 *
355 * Returns: (transfer none)(nullable): a #GtdTask
356 */
357 GtdTask*
gtd_provider_create_task_finish(GtdProvider * self,GAsyncResult * result,GError ** error)358 gtd_provider_create_task_finish (GtdProvider *self,
359 GAsyncResult *result,
360 GError **error)
361 {
362 g_return_val_if_fail (GTD_IS_PROVIDER (self), FALSE);
363 g_return_val_if_fail (!error || !*error, FALSE);
364 g_return_val_if_fail (GTD_PROVIDER_GET_IFACE (self)->create_task_finish, FALSE);
365
366 return GTD_PROVIDER_GET_IFACE (self)->create_task_finish (self, result, error);
367 }
368
369 /**
370 * gtd_provider_update_task:
371 * @provider: a #GtdProvider
372 * @task: a #GtdTask
373 * @cancellable: (nullable): a #GCancellable
374 * @callback: (scope async): a callback
375 * @user_data: (closure): user data for @callback
376 *
377 * Updates the given task in @provider.
378 */
379 void
gtd_provider_update_task(GtdProvider * provider,GtdTask * task,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)380 gtd_provider_update_task (GtdProvider *provider,
381 GtdTask *task,
382 GCancellable *cancellable,
383 GAsyncReadyCallback callback,
384 gpointer user_data)
385 {
386 g_return_if_fail (GTD_IS_PROVIDER (provider));
387 g_return_if_fail (GTD_PROVIDER_GET_IFACE (provider)->update_task);
388
389 GTD_PROVIDER_GET_IFACE (provider)->update_task (provider,
390 task,
391 cancellable,
392 callback,
393 user_data);
394 }
395
396 /**
397 * gtd_provider_update_task_finish:
398 * @self: a #GtdProvider
399 * @result: a #GAsyncResult
400 * @error: (direction out)(nullable): return location for a #GError
401 *
402 * Finishes updating the task list.
403 *
404 * Returns: %TRUE if task list was successfully updated, %FALSE otherwise
405 */
406 gboolean
gtd_provider_update_task_finish(GtdProvider * self,GAsyncResult * result,GError ** error)407 gtd_provider_update_task_finish (GtdProvider *self,
408 GAsyncResult *result,
409 GError **error)
410 {
411 g_return_val_if_fail (GTD_IS_PROVIDER (self), FALSE);
412 g_return_val_if_fail (!error || !*error, FALSE);
413 g_return_val_if_fail (GTD_PROVIDER_GET_IFACE (self)->update_task_finish, FALSE);
414
415 return GTD_PROVIDER_GET_IFACE (self)->update_task_finish (self, result, error);
416 }
417
418 /**
419 * gtd_provider_remove_task:
420 * @provider: a #GtdProvider
421 * @task: a #GtdTask
422 * @cancellable: (nullable): a #GCancellable
423 * @callback: (scope async): a callback
424 * @user_data: (closure): user data for @callback
425 *
426 * Removes the given task from @provider.
427 */
428 void
gtd_provider_remove_task(GtdProvider * provider,GtdTask * task,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)429 gtd_provider_remove_task (GtdProvider *provider,
430 GtdTask *task,
431 GCancellable *cancellable,
432 GAsyncReadyCallback callback,
433 gpointer user_data)
434 {
435 g_return_if_fail (GTD_IS_PROVIDER (provider));
436 g_return_if_fail (GTD_PROVIDER_GET_IFACE (provider)->remove_task);
437
438 GTD_PROVIDER_GET_IFACE (provider)->remove_task (provider,
439 task,
440 cancellable,
441 callback,
442 user_data);
443 }
444
445 /**
446 * gtd_provider_remove_task_finish:
447 * @self: a #GtdProvider
448 * @result: a #GAsyncResult
449 * @error: (direction out)(nullable): return location for a #GError
450 *
451 * Finishes removing the task.
452 *
453 * Returns: %TRUE if task was successfully removed, %FALSE otherwise
454 */
455 gboolean
gtd_provider_remove_task_finish(GtdProvider * self,GAsyncResult * result,GError ** error)456 gtd_provider_remove_task_finish (GtdProvider *self,
457 GAsyncResult *result,
458 GError **error)
459 {
460 g_return_val_if_fail (GTD_IS_PROVIDER (self), FALSE);
461 g_return_val_if_fail (!error || !*error, FALSE);
462 g_return_val_if_fail (GTD_PROVIDER_GET_IFACE (self)->remove_task_finish, FALSE);
463
464 return GTD_PROVIDER_GET_IFACE (self)->remove_task_finish (self, result, error);
465 }
466
467 /**
468 * gtd_provider_create_task_list:
469 * @provider: a #GtdProvider
470 * @name: (nullable): the name of the new task list
471 * @cancellable: (nullable): a #GCancellable
472 * @callback: (scope async): a callback
473 * @user_data: (closure): user data for @callback
474 *
475 * Creates the given list in @provider.
476 */
477 void
gtd_provider_create_task_list(GtdProvider * provider,const gchar * name,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)478 gtd_provider_create_task_list (GtdProvider *provider,
479 const gchar *name,
480 GCancellable *cancellable,
481 GAsyncReadyCallback callback,
482 gpointer user_data)
483 {
484 g_return_if_fail (GTD_IS_PROVIDER (provider));
485 g_return_if_fail (GTD_PROVIDER_GET_IFACE (provider)->create_task_list);
486
487 GTD_PROVIDER_GET_IFACE (provider)->create_task_list (provider,
488 name,
489 cancellable,
490 callback,
491 user_data);
492 }
493
494 /**
495 * gtd_provider_create_task_list_finish:
496 * @self: a #GtdProvider
497 * @result: a #GAsyncResult
498 * @error: (direction out)(nullable): return location for a #GError
499 *
500 * Finishes creating the task list. The provider will emit the
501 * GtdProvider:list-added signal after creating the task list.
502 *
503 * Returns: %TRUE if task list was successfully created, %FALSE otherwise
504 */
505 gboolean
gtd_provider_create_task_list_finish(GtdProvider * self,GAsyncResult * result,GError ** error)506 gtd_provider_create_task_list_finish (GtdProvider *self,
507 GAsyncResult *result,
508 GError **error)
509 {
510 g_return_val_if_fail (GTD_IS_PROVIDER (self), FALSE);
511 g_return_val_if_fail (!error || !*error, FALSE);
512 g_return_val_if_fail (GTD_PROVIDER_GET_IFACE (self)->create_task_list_finish, FALSE);
513
514 return GTD_PROVIDER_GET_IFACE (self)->create_task_list_finish (self, result, error);
515 }
516
517 /**
518 * gtd_provider_update_task_list:
519 * @provider: a #GtdProvider
520 * @list: a #GtdTaskList
521 * @cancellable: (nullable): a #GCancellable
522 * @callback: (scope async): a callback
523 * @user_data: (closure): user data for @callback
524 *
525 * Updates the given list in @provider.
526 */
527 void
gtd_provider_update_task_list(GtdProvider * provider,GtdTaskList * list,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)528 gtd_provider_update_task_list (GtdProvider *provider,
529 GtdTaskList *list,
530 GCancellable *cancellable,
531 GAsyncReadyCallback callback,
532 gpointer user_data)
533 {
534 g_return_if_fail (GTD_IS_PROVIDER (provider));
535 g_return_if_fail (GTD_PROVIDER_GET_IFACE (provider)->update_task_list);
536
537 GTD_PROVIDER_GET_IFACE (provider)->update_task_list (provider,
538 list,
539 cancellable,
540 callback,
541 user_data);
542 }
543
544 /**
545 * gtd_provider_create_task_list_finish:
546 * @self: a #GtdProvider
547 * @result: a #GAsyncResult
548 * @error: (direction out)(nullable): return location for a #GError
549 *
550 * Finishes updating the task list. The provider will emit the
551 * GtdProvider:list-updated signal after updating the task list.
552 *
553 * Returns: %TRUE if task list was successfully created, %FALSE otherwise
554 */
555 gboolean
gtd_provider_update_task_list_finish(GtdProvider * self,GAsyncResult * result,GError ** error)556 gtd_provider_update_task_list_finish (GtdProvider *self,
557 GAsyncResult *result,
558 GError **error)
559 {
560 g_return_val_if_fail (GTD_IS_PROVIDER (self), FALSE);
561 g_return_val_if_fail (!error || !*error, FALSE);
562 g_return_val_if_fail (GTD_PROVIDER_GET_IFACE (self)->update_task_list_finish, FALSE);
563
564 return GTD_PROVIDER_GET_IFACE (self)->update_task_list_finish (self, result, error);
565 }
566
567 /**
568 * gtd_provider_remove_task_list:
569 * @provider: a #GtdProvider
570 * @list: a #GtdTaskList
571 * @cancellable: (nullable): a #GCancellable
572 * @callback: (scope async): a callback
573 * @user_data: (closure): user data for @callback
574 *
575 * Removes the given list from @provider.
576 */
577 void
gtd_provider_remove_task_list(GtdProvider * provider,GtdTaskList * list,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)578 gtd_provider_remove_task_list (GtdProvider *provider,
579 GtdTaskList *list,
580 GCancellable *cancellable,
581 GAsyncReadyCallback callback,
582 gpointer user_data)
583 {
584 g_return_if_fail (GTD_IS_PROVIDER (provider));
585 g_return_if_fail (GTD_PROVIDER_GET_IFACE (provider)->remove_task_list);
586
587 GTD_PROVIDER_GET_IFACE (provider)->remove_task_list (provider,
588 list,
589 cancellable,
590 callback,
591 user_data);
592 }
593
594 /**
595 * gtd_provider_remove_task_list_finish:
596 * @self: a #GtdProvider
597 * @result: a #GAsyncResult
598 * @error: (direction out)(nullable): return location for a #GError
599 *
600 * Finishes removing the task list. The provider will emit the
601 * GtdProvider:list-removed signal after removing the task list.
602 *
603 * Returns: %TRUE if task list was successfully removed, %FALSE otherwise
604 */
605 gboolean
gtd_provider_remove_task_list_finish(GtdProvider * self,GAsyncResult * result,GError ** error)606 gtd_provider_remove_task_list_finish (GtdProvider *self,
607 GAsyncResult *result,
608 GError **error)
609 {
610 g_return_val_if_fail (GTD_IS_PROVIDER (self), FALSE);
611 g_return_val_if_fail (!error || !*error, FALSE);
612 g_return_val_if_fail (GTD_PROVIDER_GET_IFACE (self)->remove_task_list_finish, FALSE);
613
614 return GTD_PROVIDER_GET_IFACE (self)->remove_task_list_finish (self, result, error);
615 }
616
617 /**
618 * gtd_provider_get_task_lists:
619 * @provider: a #GtdProvider
620 *
621 * Retrieves the tasklists that this provider contains.
622 *
623 * Returns: (transfer container) (element-type Gtd.TaskList): the list of tasks, or %NULL
624 */
625 GList*
gtd_provider_get_task_lists(GtdProvider * provider)626 gtd_provider_get_task_lists (GtdProvider *provider)
627 {
628 g_return_val_if_fail (GTD_IS_PROVIDER (provider), NULL);
629 g_return_val_if_fail (GTD_PROVIDER_GET_IFACE (provider)->get_task_lists, NULL);
630
631 return GTD_PROVIDER_GET_IFACE (provider)->get_task_lists (provider);
632 }
633
634 /**
635 * gtd_provider_get_inbox:
636 * @provider: a #GtdProvider
637 *
638 * Retrieves the inbox of @provider.
639 *
640 * Returns: (transfer none)(nullable): a #GtdTaskList
641 */
642 GtdTaskList*
gtd_provider_get_inbox(GtdProvider * provider)643 gtd_provider_get_inbox (GtdProvider *provider)
644 {
645 g_return_val_if_fail (GTD_IS_PROVIDER (provider), NULL);
646 g_return_val_if_fail (GTD_PROVIDER_GET_IFACE (provider)->get_inbox, NULL);
647
648 return GTD_PROVIDER_GET_IFACE (provider)->get_inbox (provider);
649 }
650
651 /**
652 * gtd_provider_compare:
653 * @a: a #GtdProvider
654 * @b: a #GtdProvider
655 *
656 * Compares @a and @b. The sorting criteria is internal and
657 * may change.
658 *
659 * Returns: -1 if @a comes before @b, 1 for the oposite, and
660 * 0 if they're equal
661 */
662 gint
gtd_provider_compare(GtdProvider * a,GtdProvider * b)663 gtd_provider_compare (GtdProvider *a,
664 GtdProvider *b)
665 {
666 gint result;
667
668 g_return_val_if_fail (GTD_IS_PROVIDER (a), 0);
669 g_return_val_if_fail (GTD_IS_PROVIDER (b), 0);
670
671 if (a == b)
672 return 0;
673
674 result = gtd_collate_compare_strings (gtd_provider_get_name (a), gtd_provider_get_name (b));
675
676 if (result != 0)
677 return result;
678
679 return gtd_collate_compare_strings (gtd_provider_get_description (a), gtd_provider_get_description (b));
680 }
681