1 /* ide-project-info.c
2  *
3  * Copyright 2015-2019 Christian Hergert <christian@hergert.me>
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  * SPDX-License-Identifier: GPL-3.0-or-later
19  */
20 
21 #ifndef _GNU_SOURCE
22 # define _GNU_SOURCE
23 #endif
24 
25 #define G_LOG_DOMAIN "ide-project-info"
26 
27 #include "config.h"
28 
29 #include <glib/gi18n.h>
30 #include <string.h>
31 
32 #include "ide-gfile-private.h"
33 
34 #include "ide-project-info.h"
35 #include "ide-project-info-private.h"
36 
37 /**
38  * SECTION:ideprojectinfo:
39  * @title: IdeProjectInfo
40  * @short_description: Information about a project not yet loaded
41  *
42  * This class contains information about a project that can be loaded.
43  * This information should be used to display a list of available projects.
44  *
45  * Since: 3.32
46  */
47 
48 struct _IdeProjectInfo
49 {
50   GObject     parent_instance;
51 
52   gchar      *id;
53   IdeDoap    *doap;
54   GDateTime  *last_modified_at;
55   GFile      *directory;
56   GFile      *directory_translated;
57   GFile      *file;
58   GFile      *file_translated;
59   gchar      *build_system_name;
60   gchar      *build_system_hint;
61   gchar      *name;
62   gchar      *description;
63   gchar     **languages;
64   gchar      *vcs_uri;
65   GIcon      *icon;
66 
67   gint        priority;
68 
69   guint       is_recent : 1;
70 };
71 
72 G_DEFINE_FINAL_TYPE (IdeProjectInfo, ide_project_info, G_TYPE_OBJECT)
73 
74 enum {
75   PROP_0,
76   PROP_BUILD_SYSTEM_HINT,
77   PROP_BUILD_SYSTEM_NAME,
78   PROP_DESCRIPTION,
79   PROP_DIRECTORY,
80   PROP_DOAP,
81   PROP_FILE,
82   PROP_ICON,
83   PROP_ICON_NAME,
84   PROP_ID,
85   PROP_IS_RECENT,
86   PROP_LANGUAGES,
87   PROP_LAST_MODIFIED_AT,
88   PROP_NAME,
89   PROP_PRIORITY,
90   PROP_VCS_URI,
91   LAST_PROP
92 };
93 
94 static GParamSpec *properties [LAST_PROP];
95 
96 /**
97  * ide_project_info_get_doap:
98  *
99  *
100  * Returns: (nullable) (transfer none): An #IdeDoap or %NULL.
101  *
102  * Since: 3.32
103  */
104 IdeDoap *
ide_project_info_get_doap(IdeProjectInfo * self)105 ide_project_info_get_doap (IdeProjectInfo *self)
106 {
107   g_return_val_if_fail (IDE_IS_PROJECT_INFO (self), NULL);
108 
109   return self->doap;
110 }
111 
112 void
ide_project_info_set_doap(IdeProjectInfo * self,IdeDoap * doap)113 ide_project_info_set_doap (IdeProjectInfo *self,
114                            IdeDoap        *doap)
115 {
116   g_return_if_fail (IDE_IS_PROJECT_INFO (self));
117   g_return_if_fail (!doap || IDE_IS_DOAP (doap));
118 
119   if (g_set_object (&self->doap, doap))
120     g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_DOAP]);
121 }
122 
123 /**
124  * ide_project_info_get_languages:
125  *
126  * Returns: (transfer none): An array of language names.
127  *
128  * Since: 3.32
129  */
130 const gchar * const *
ide_project_info_get_languages(IdeProjectInfo * self)131 ide_project_info_get_languages (IdeProjectInfo *self)
132 {
133   g_return_val_if_fail (IDE_IS_PROJECT_INFO (self), NULL);
134 
135   return (const gchar * const *)self->languages;
136 }
137 
138 void
ide_project_info_set_languages(IdeProjectInfo * self,gchar ** languages)139 ide_project_info_set_languages (IdeProjectInfo  *self,
140                                 gchar          **languages)
141 {
142   g_return_if_fail (IDE_IS_PROJECT_INFO (self));
143 
144   if (languages != self->languages)
145     {
146       g_strfreev (self->languages);
147       self->languages = g_strdupv (languages);
148       g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_LANGUAGES]);
149     }
150 }
151 
152 gint
ide_project_info_get_priority(IdeProjectInfo * self)153 ide_project_info_get_priority (IdeProjectInfo *self)
154 {
155   g_return_val_if_fail (IDE_IS_PROJECT_INFO (self), 0);
156 
157   return self->priority;
158 }
159 
160 void
ide_project_info_set_priority(IdeProjectInfo * self,gint priority)161 ide_project_info_set_priority (IdeProjectInfo *self,
162                                gint            priority)
163 {
164   g_return_if_fail (IDE_IS_PROJECT_INFO (self));
165 
166   if (self->priority != priority)
167     {
168       self->priority = priority;
169       g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_PRIORITY]);
170     }
171 }
172 
173 /**
174  * ide_project_info_get_directory:
175  * @self: (in): an #IdeProjectInfo.
176  *
177  * Gets the #IdeProjectInfo:directory property.
178  * This is the directory containing the project (if known).
179  *
180  * Returns: (nullable) (transfer none): a #GFile.
181  *
182  * Since: 3.32
183  */
184 GFile *
ide_project_info_get_directory(IdeProjectInfo * self)185 ide_project_info_get_directory (IdeProjectInfo *self)
186 {
187   g_return_val_if_fail (IDE_IS_PROJECT_INFO (self), NULL);
188 
189   return self->directory_translated;
190 }
191 
192 /**
193  * ide_project_info_get_file:
194  * @self: (in): an #IdeProjectInfo.
195  *
196  * Gets the #IdeProjectInfo:file property.
197  * This is the project file (such as configure.ac) of the project.
198  *
199  * Returns: (nullable) (transfer none): a #GFile.
200  *
201  * Since: 3.32
202  */
203 GFile *
ide_project_info_get_file(IdeProjectInfo * self)204 ide_project_info_get_file (IdeProjectInfo *self)
205 {
206   g_return_val_if_fail (IDE_IS_PROJECT_INFO (self), NULL);
207 
208   return self->file_translated;
209 }
210 
211 /**
212  * ide_project_info_get_last_modified_at:
213  *
214  *
215  * Returns: (transfer none) (nullable): a #GDateTime or %NULL.
216  *
217  * Since: 3.32
218  */
219 GDateTime *
ide_project_info_get_last_modified_at(IdeProjectInfo * self)220 ide_project_info_get_last_modified_at (IdeProjectInfo *self)
221 {
222   g_return_val_if_fail (IDE_IS_PROJECT_INFO (self), NULL);
223 
224   return self->last_modified_at;
225 }
226 
227 const gchar *
ide_project_info_get_build_system_hint(IdeProjectInfo * self)228 ide_project_info_get_build_system_hint (IdeProjectInfo *self)
229 {
230   g_return_val_if_fail (IDE_IS_PROJECT_INFO (self), NULL);
231 
232   return self->build_system_hint;
233 }
234 
235 void
ide_project_info_set_build_system_hint(IdeProjectInfo * self,const gchar * build_system_hint)236 ide_project_info_set_build_system_hint (IdeProjectInfo *self,
237                                         const gchar    *build_system_hint)
238 {
239   g_return_if_fail (IDE_IS_PROJECT_INFO (self));
240 
241   if (!ide_str_equal0 (self->build_system_hint, build_system_hint))
242     {
243       g_free (self->build_system_hint);
244       self->build_system_hint = g_strdup (build_system_hint);
245       g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_BUILD_SYSTEM_HINT]);
246     }
247 }
248 
249 const gchar *
ide_project_info_get_build_system_name(IdeProjectInfo * self)250 ide_project_info_get_build_system_name (IdeProjectInfo *self)
251 {
252   g_return_val_if_fail (IDE_IS_PROJECT_INFO (self), NULL);
253 
254   return self->build_system_name;
255 }
256 
257 void
ide_project_info_set_build_system_name(IdeProjectInfo * self,const gchar * build_system_name)258 ide_project_info_set_build_system_name (IdeProjectInfo *self,
259                                         const gchar    *build_system_name)
260 {
261   g_return_if_fail (IDE_IS_PROJECT_INFO (self));
262 
263   if (!ide_str_equal0 (self->build_system_name, build_system_name))
264     {
265       g_free (self->build_system_name);
266       self->build_system_name = g_strdup (build_system_name);
267       g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_BUILD_SYSTEM_NAME]);
268     }
269 }
270 
271 const gchar *
ide_project_info_get_description(IdeProjectInfo * self)272 ide_project_info_get_description (IdeProjectInfo *self)
273 {
274   g_return_val_if_fail (IDE_IS_PROJECT_INFO (self), NULL);
275 
276   return self->description;
277 }
278 
279 void
ide_project_info_set_description(IdeProjectInfo * self,const gchar * description)280 ide_project_info_set_description (IdeProjectInfo *self,
281                                   const gchar    *description)
282 {
283   g_return_if_fail (IDE_IS_PROJECT_INFO (self));
284 
285   if (!ide_str_equal0 (self->description, description))
286     {
287       g_free (self->description);
288       self->description = g_strdup (description);
289       g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_DESCRIPTION]);
290     }
291 }
292 
293 const gchar *
ide_project_info_get_name(IdeProjectInfo * self)294 ide_project_info_get_name (IdeProjectInfo *self)
295 {
296   g_return_val_if_fail (IDE_IS_PROJECT_INFO (self), NULL);
297 
298   return self->name;
299 }
300 
301 void
ide_project_info_set_name(IdeProjectInfo * self,const gchar * name)302 ide_project_info_set_name (IdeProjectInfo *self,
303                            const gchar    *name)
304 {
305   g_return_if_fail (IDE_IS_PROJECT_INFO (self));
306 
307   if (!ide_str_equal0 (self->name, name))
308     {
309       g_free (self->name);
310       self->name = g_strdup (name);
311       g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_NAME]);
312     }
313 }
314 
315 void
ide_project_info_set_directory(IdeProjectInfo * self,GFile * directory)316 ide_project_info_set_directory (IdeProjectInfo *self,
317                                 GFile          *directory)
318 {
319   g_return_if_fail (IDE_IS_PROJECT_INFO (self));
320   g_return_if_fail (!directory || G_IS_FILE (directory));
321 
322   if (g_set_object (&self->directory, directory))
323     {
324       g_clear_object (&self->directory_translated);
325       self->directory_translated = _ide_g_file_readlink (directory);
326       g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_DIRECTORY]);
327     }
328 }
329 
330 void
ide_project_info_set_file(IdeProjectInfo * self,GFile * file)331 ide_project_info_set_file (IdeProjectInfo *self,
332                            GFile          *file)
333 {
334   g_return_if_fail (IDE_IS_PROJECT_INFO (self));
335   g_return_if_fail (!file || G_IS_FILE (file));
336 
337   if (g_set_object (&self->file, file))
338     {
339       g_clear_object (&self->file_translated);
340       self->file_translated = _ide_g_file_readlink (file);
341       g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_FILE]);
342     }
343 }
344 
345 void
ide_project_info_set_last_modified_at(IdeProjectInfo * self,GDateTime * last_modified_at)346 ide_project_info_set_last_modified_at (IdeProjectInfo *self,
347                                        GDateTime      *last_modified_at)
348 {
349   g_assert (IDE_IS_PROJECT_INFO (self));
350 
351   if (last_modified_at != self->last_modified_at)
352     {
353       g_clear_pointer (&self->last_modified_at, g_date_time_unref);
354       self->last_modified_at = last_modified_at ? g_date_time_ref (last_modified_at) : NULL;
355       g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_LAST_MODIFIED_AT]);
356     }
357 }
358 
359 gboolean
ide_project_info_get_is_recent(IdeProjectInfo * self)360 ide_project_info_get_is_recent (IdeProjectInfo *self)
361 {
362   g_return_val_if_fail (IDE_IS_PROJECT_INFO (self), FALSE);
363 
364   return self->is_recent;
365 }
366 
367 void
ide_project_info_set_is_recent(IdeProjectInfo * self,gboolean is_recent)368 ide_project_info_set_is_recent (IdeProjectInfo *self,
369                                 gboolean        is_recent)
370 {
371   g_return_if_fail (IDE_IS_PROJECT_INFO (self));
372 
373   is_recent = !!is_recent;
374 
375   if (self->is_recent != is_recent)
376     {
377       self->is_recent = is_recent;
378       g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_IS_RECENT]);
379     }
380 }
381 
382 static void
ide_project_info_finalize(GObject * object)383 ide_project_info_finalize (GObject *object)
384 {
385   IdeProjectInfo *self = (IdeProjectInfo *)object;
386 
387   g_clear_pointer (&self->id, g_free);
388   g_clear_pointer (&self->last_modified_at, g_date_time_unref);
389   g_clear_pointer (&self->build_system_hint, g_free);
390   g_clear_pointer (&self->build_system_name, g_free);
391   g_clear_pointer (&self->description, g_free);
392   g_clear_pointer (&self->languages, g_strfreev);
393   g_clear_pointer (&self->vcs_uri, g_free);
394   g_clear_pointer (&self->name, g_free);
395   g_clear_object (&self->directory);
396   g_clear_object (&self->directory_translated);
397   g_clear_object (&self->file);
398   g_clear_object (&self->file_translated);
399   g_clear_object (&self->icon);
400 
401   G_OBJECT_CLASS (ide_project_info_parent_class)->finalize (object);
402 }
403 
404 static void
ide_project_info_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)405 ide_project_info_get_property (GObject    *object,
406                                guint       prop_id,
407                                GValue     *value,
408                                GParamSpec *pspec)
409 {
410   IdeProjectInfo *self = IDE_PROJECT_INFO (object);
411 
412   switch (prop_id)
413     {
414     case PROP_BUILD_SYSTEM_HINT:
415       g_value_set_string (value, ide_project_info_get_build_system_hint (self));
416       break;
417 
418     case PROP_BUILD_SYSTEM_NAME:
419       g_value_set_string (value, ide_project_info_get_build_system_name (self));
420       break;
421 
422     case PROP_DESCRIPTION:
423       g_value_set_string (value, ide_project_info_get_description (self));
424       break;
425 
426     case PROP_DIRECTORY:
427       g_value_set_object (value, ide_project_info_get_directory (self));
428       break;
429 
430     case PROP_DOAP:
431       g_value_set_object (value, ide_project_info_get_doap (self));
432       break;
433 
434     case PROP_FILE:
435       g_value_set_object (value, ide_project_info_get_file (self));
436       break;
437 
438     case PROP_ICON:
439       g_value_set_object (value, ide_project_info_get_icon (self));
440       break;
441 
442     case PROP_ID:
443       g_value_set_string (value, ide_project_info_get_id (self));
444       break;
445 
446     case PROP_IS_RECENT:
447       g_value_set_boolean (value, ide_project_info_get_is_recent (self));
448       break;
449 
450     case PROP_LANGUAGES:
451       g_value_set_boxed (value, ide_project_info_get_languages (self));
452       break;
453 
454     case PROP_LAST_MODIFIED_AT:
455       g_value_set_boxed (value, ide_project_info_get_last_modified_at (self));
456       break;
457 
458     case PROP_NAME:
459       g_value_set_string (value, ide_project_info_get_name (self));
460       break;
461 
462     case PROP_PRIORITY:
463       g_value_set_int (value, ide_project_info_get_priority (self));
464       break;
465 
466     case PROP_VCS_URI:
467       g_value_set_string (value, ide_project_info_get_vcs_uri (self));
468       break;
469 
470     default:
471       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
472     }
473 }
474 
475 static void
ide_project_info_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)476 ide_project_info_set_property (GObject      *object,
477                                guint         prop_id,
478                                const GValue *value,
479                                GParamSpec   *pspec)
480 {
481   IdeProjectInfo *self = IDE_PROJECT_INFO (object);
482 
483   switch (prop_id)
484     {
485     case PROP_BUILD_SYSTEM_HINT:
486       ide_project_info_set_build_system_hint (self, g_value_get_string (value));
487       break;
488 
489     case PROP_BUILD_SYSTEM_NAME:
490       ide_project_info_set_build_system_name (self, g_value_get_string (value));
491       break;
492 
493     case PROP_DESCRIPTION:
494       ide_project_info_set_description (self, g_value_get_string (value));
495       break;
496 
497     case PROP_DIRECTORY:
498       ide_project_info_set_directory (self, g_value_get_object (value));
499       break;
500 
501     case PROP_DOAP:
502       ide_project_info_set_doap (self, g_value_get_object (value));
503       break;
504 
505     case PROP_FILE:
506       ide_project_info_set_file (self, g_value_get_object (value));
507       break;
508 
509     case PROP_ICON:
510       ide_project_info_set_icon (self, g_value_get_object (value));
511       break;
512 
513     case PROP_ICON_NAME:
514       ide_project_info_set_icon_name (self, g_value_get_string (value));
515       break;
516 
517     case PROP_ID:
518       ide_project_info_set_id (self, g_value_get_string (value));
519       break;
520 
521     case PROP_IS_RECENT:
522       ide_project_info_set_is_recent (self, g_value_get_boolean (value));
523       break;
524 
525     case PROP_LANGUAGES:
526       ide_project_info_set_languages (self, g_value_get_boxed (value));
527       break;
528 
529     case PROP_LAST_MODIFIED_AT:
530       ide_project_info_set_last_modified_at (self, g_value_get_boxed (value));
531       break;
532 
533     case PROP_NAME:
534       ide_project_info_set_name (self, g_value_get_string (value));
535       break;
536 
537     case PROP_PRIORITY:
538       ide_project_info_set_priority (self, g_value_get_int (value));
539       break;
540 
541     case PROP_VCS_URI:
542       ide_project_info_set_vcs_uri (self, g_value_get_string (value));
543       break;
544 
545     default:
546       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
547     }
548 }
549 
550 static void
ide_project_info_class_init(IdeProjectInfoClass * klass)551 ide_project_info_class_init (IdeProjectInfoClass *klass)
552 {
553   GObjectClass *object_class = G_OBJECT_CLASS (klass);
554 
555   object_class->finalize = ide_project_info_finalize;
556   object_class->get_property = ide_project_info_get_property;
557   object_class->set_property = ide_project_info_set_property;
558 
559   properties [PROP_BUILD_SYSTEM_HINT] =
560     g_param_spec_string ("build-system-hint",
561                          "Build System hint",
562                          "Build System hint",
563                          NULL,
564                          (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
565 
566   properties [PROP_BUILD_SYSTEM_NAME] =
567     g_param_spec_string ("build-system-name",
568                          "Build System name",
569                          "Build System name",
570                          NULL,
571                          (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
572 
573   properties [PROP_DESCRIPTION] =
574     g_param_spec_string ("description",
575                          "Description",
576                          "The project description.",
577                          NULL,
578                          (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
579 
580   properties [PROP_ICON] =
581     g_param_spec_object ("icon",
582                          "Icon",
583                          "The icon for the project",
584                          G_TYPE_ICON,
585                          (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
586 
587   properties [PROP_ICON_NAME] =
588     g_param_spec_string ("icon-name",
589                          "Icon Name",
590                          "The icon-name for the project",
591                          NULL,
592                          (G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
593 
594   properties [PROP_ID] =
595     g_param_spec_string ("id",
596                          "Id",
597                          "The identifier for the project",
598                          NULL,
599                          (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
600 
601   properties [PROP_NAME] =
602     g_param_spec_string ("name",
603                          "Name",
604                          "The project name.",
605                          NULL,
606                          (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
607 
608   properties [PROP_DIRECTORY] =
609     g_param_spec_object ("directory",
610                          "Directory",
611                          "The project directory.",
612                          G_TYPE_FILE,
613                          (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
614 
615   properties [PROP_DOAP] =
616     g_param_spec_object ("doap",
617                          "DOAP",
618                          "A DOAP describing the project.",
619                          IDE_TYPE_DOAP,
620                          (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
621 
622   properties [PROP_FILE] =
623     g_param_spec_object ("file",
624                          "File",
625                          "The toplevel project file.",
626                          G_TYPE_FILE,
627                          (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
628 
629   properties [PROP_IS_RECENT] =
630     g_param_spec_boolean ("is-recent",
631                           "Is Recent",
632                           "Is Recent",
633                           FALSE,
634                           (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
635 
636   properties [PROP_LANGUAGES] =
637     g_param_spec_boxed ("languages",
638                         "Languages",
639                         "Languages",
640                         G_TYPE_STRV,
641                         (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
642 
643   properties [PROP_LAST_MODIFIED_AT] =
644     g_param_spec_boxed ("last-modified-at",
645                         "Last Modified At",
646                         "Last Modified At",
647                         G_TYPE_DATE_TIME,
648                         (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
649 
650   properties [PROP_PRIORITY] =
651     g_param_spec_int ("priority",
652                       "Priority",
653                       "The priority of the project information type.",
654                       G_MININT,
655                       G_MAXINT,
656                       0,
657                       (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
658 
659   properties [PROP_VCS_URI] =
660     g_param_spec_string ("vcs-uri",
661                          "Vcs Uri",
662                          "The VCS URI of the project, in case it is not local",
663                          NULL,
664                          (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
665 
666   g_object_class_install_properties (object_class, LAST_PROP, properties);
667 }
668 
669 static void
ide_project_info_init(IdeProjectInfo * self)670 ide_project_info_init (IdeProjectInfo *self)
671 {
672 }
673 
674 gint
ide_project_info_compare(IdeProjectInfo * info1,IdeProjectInfo * info2)675 ide_project_info_compare (IdeProjectInfo *info1,
676                           IdeProjectInfo *info2)
677 {
678   const gchar *name1;
679   const gchar *name2;
680   GDateTime *dt1;
681   GDateTime *dt2;
682   gint ret;
683   gint prio1;
684   gint prio2;
685 
686   g_assert (IDE_IS_PROJECT_INFO (info1));
687   g_assert (IDE_IS_PROJECT_INFO (info2));
688 
689   if (info1 == info2)
690     return 0;
691 
692   prio1 = ide_project_info_get_priority (info1);
693   prio2 = ide_project_info_get_priority (info2);
694 
695   if (prio1 != prio2)
696     return prio1 - prio2;
697 
698   dt1 = ide_project_info_get_last_modified_at (info1);
699   dt2 = ide_project_info_get_last_modified_at (info2);
700 
701   ret = g_date_time_compare (dt2, dt1);
702 
703   if (ret != 0)
704     return ret;
705 
706   name1 = ide_project_info_get_name (info1);
707   name2 = ide_project_info_get_name (info2);
708 
709   if (name1 == NULL)
710     return 1;
711   else if (name2 == NULL)
712     return -1;
713   else
714     return strcasecmp (name1, name2);
715 }
716 
717 /**
718  * ide_project_info_get_vcs_uri:
719  * @self: an #IdeProjectInfo
720  *
721  * Gets the VCS URI for the project info. This should be set with the
722  * remote URI for the version control system. It can be used to clone the
723  * project when activated from the greeter.
724  *
725  * Returns: (transfer none) (nullable): a #IdeVcsUri or %NULL
726  *
727  * Since: 3.32
728  */
729 const gchar *
ide_project_info_get_vcs_uri(IdeProjectInfo * self)730 ide_project_info_get_vcs_uri (IdeProjectInfo *self)
731 {
732   g_return_val_if_fail (IDE_IS_PROJECT_INFO (self), NULL);
733 
734   return self->vcs_uri;
735 }
736 
737 void
ide_project_info_set_vcs_uri(IdeProjectInfo * self,const gchar * vcs_uri)738 ide_project_info_set_vcs_uri (IdeProjectInfo *self,
739                               const gchar    *vcs_uri)
740 {
741   g_return_if_fail (IDE_IS_PROJECT_INFO (self));
742 
743   if (!ide_str_equal0 (self->vcs_uri, vcs_uri))
744     {
745       g_free (self->vcs_uri);
746       self->vcs_uri = g_strdup (vcs_uri);
747       g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_VCS_URI]);
748     }
749 }
750 
751 IdeProjectInfo *
ide_project_info_new(void)752 ide_project_info_new (void)
753 {
754   return g_object_new (IDE_TYPE_PROJECT_INFO, NULL);
755 }
756 
757 /**
758  * ide_project_info_equal:
759  * @self: a #IdeProjectInfo
760  * @other: a #IdeProjectInfo
761  *
762  * This function will check to see if information about @self and @other are
763  * similar enough that a request to open @other would instead activate
764  * @self. This is useful when a user tries to open the same project twice.
765  *
766  * However, some case is taken to ensure that things like the build system
767  * are the same so that a project may be opened twice with two build systems
768  * as is sometimes necessary when projects are porting to a new build
769  * system.
770  *
771  * Returns: %TRUE if @self and @other are the same project and similar
772  *   enough to be considered equal.
773  *
774  * Since: 3.32
775  */
776 gboolean
ide_project_info_equal(IdeProjectInfo * self,IdeProjectInfo * other)777 ide_project_info_equal (IdeProjectInfo *self,
778                         IdeProjectInfo *other)
779 {
780   g_return_val_if_fail (IDE_IS_PROJECT_INFO (self), FALSE);
781   g_return_val_if_fail (IDE_IS_PROJECT_INFO (other), FALSE);
782 
783   if (!self->file || !other->file ||
784       !g_file_equal (self->file, other->file))
785     {
786       if (!self->directory || !other->directory ||
787           !g_file_equal (self->directory, other->directory))
788         return FALSE;
789     }
790 
791   /* build-system only set in one of the project-info?
792    * That's fine, we'll consider them the same to avoid over
793    * activating a second workbench
794    */
795   if ((!self->build_system_name && other->build_system_name) ||
796       (self->build_system_name && !other->build_system_name))
797     return TRUE;
798 
799   return ide_str_equal0 (self->build_system_name, other->build_system_name);
800 }
801 
802 const gchar *
ide_project_info_get_id(IdeProjectInfo * self)803 ide_project_info_get_id (IdeProjectInfo *self)
804 {
805   g_return_val_if_fail (IDE_IS_PROJECT_INFO (self), NULL);
806 
807   if (!self->id && self->directory)
808     self->id = g_file_get_basename (self->directory);
809 
810   if (!self->id && self->file)
811     {
812       if (g_file_query_file_type (self->file, 0, NULL) == G_FILE_TYPE_DIRECTORY)
813         self->id = g_file_get_basename (self->file);
814       else
815         {
816           g_autoptr(GFile) parent = g_file_get_parent (self->file);
817           self->id = g_file_get_basename (parent);
818         }
819     }
820 
821   if (!self->id && self->doap)
822     self->id = g_strdup (ide_doap_get_name (self->doap));
823 
824   if (!self->id && self->vcs_uri)
825     {
826       const gchar *path = self->vcs_uri;
827 
828       if (strstr (path, "//"))
829         path = strstr (path, "//") + 1;
830 
831       if (strchr (path, '/'))
832         path = strchr (path, '/');
833       else if (strrchr (path, ':'))
834         path = strrchr (path, ':');
835 
836       self->id = g_path_get_basename (path);
837     }
838 
839   return self->id;
840 }
841 
842 void
ide_project_info_set_id(IdeProjectInfo * self,const gchar * id)843 ide_project_info_set_id (IdeProjectInfo *self,
844                          const gchar    *id)
845 {
846   g_return_if_fail (IDE_IS_PROJECT_INFO (self));
847 
848   if (!ide_str_equal0 (id, self->id))
849     {
850       g_free (self->id);
851       self->id = g_strdup (id);
852       g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_ID]);
853     }
854 }
855 
856 /**
857  * ide_project_info_get_icon:
858  * @self: a #IdeProjectInfo
859  *
860  * Gets the #IdeProjectInfo:icon property.
861  *
862  * Returns: (transfer none) (nullable): a #GIcon or %NULL
863  *
864  * Since: 3.32
865  */
866 GIcon *
ide_project_info_get_icon(IdeProjectInfo * self)867 ide_project_info_get_icon (IdeProjectInfo *self)
868 {
869   g_return_val_if_fail (IDE_IS_PROJECT_INFO (self), NULL);
870 
871   return self->icon;
872 }
873 
874 void
ide_project_info_set_icon(IdeProjectInfo * self,GIcon * icon)875 ide_project_info_set_icon (IdeProjectInfo *self,
876                            GIcon          *icon)
877 {
878   g_return_if_fail (IDE_IS_PROJECT_INFO (self));
879   g_return_if_fail (!icon || G_IS_ICON (icon));
880 
881   if (g_set_object (&self->icon, icon))
882     g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_ICON]);
883 }
884 
885 void
ide_project_info_set_icon_name(IdeProjectInfo * self,const gchar * icon_name)886 ide_project_info_set_icon_name (IdeProjectInfo *self,
887                                 const gchar    *icon_name)
888 {
889   g_autoptr(GIcon) icon = NULL;
890 
891   g_return_if_fail (IDE_IS_PROJECT_INFO (self));
892 
893   if (icon_name != NULL)
894     icon = g_themed_icon_new (icon_name);
895   ide_project_info_set_icon (self, icon);
896 }
897 
898 GFile *
_ide_project_info_get_real_file(IdeProjectInfo * self)899 _ide_project_info_get_real_file (IdeProjectInfo *self)
900 {
901   g_return_val_if_fail (IDE_IS_PROJECT_INFO (self), NULL);
902   return self->file;
903 }
904 
905 GFile *
_ide_project_info_get_real_directory(IdeProjectInfo * self)906 _ide_project_info_get_real_directory (IdeProjectInfo *self)
907 {
908   g_return_val_if_fail (IDE_IS_PROJECT_INFO (self), NULL);
909   return self->directory;
910 }
911