1 /*
2  * Copyright 2017 LarsGit223
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  */
18 
19 /*
20  * Code for the WORKBENCH structure.
21  */
22 #ifdef HAVE_CONFIG_H
23 # include "config.h"
24 #endif
25 
26 #include <glib.h>
27 #include <glib/gstdio.h>
28 #include <geanyplugin.h>
29 #include "workbench.h"
30 #include "sidebar.h"
31 #include "wb_project.h"
32 #include "wb_monitor.h"
33 #include "utils.h"
34 
35 typedef struct
36 {
37 	PROJECT_ENTRY_STATUS status;
38 	gchar                *abs_filename;
39 	gchar                *rel_filename;
40 	gboolean             use_abs;
41 	WB_PROJECT           *project;
42 }WB_PROJECT_ENTRY;
43 
44 struct S_WORKBENCH
45 {
46 	gchar     *filename;
47 	gchar     *name;
48 	gboolean  modified;
49 	gboolean  rescan_projects_on_open;
50 	gboolean  enable_live_update;
51 	gboolean  expand_on_hover;
52 	gboolean  enable_tree_lines;
53 	GPtrArray *projects;
54 	GPtrArray *bookmarks;
55 	WB_MONITOR *monitor;
56 };
57 
58 /* Create a new, empty workbench project entry */
wb_project_entry_new(void)59 static WB_PROJECT_ENTRY *wb_project_entry_new(void)
60 {
61 	WB_PROJECT_ENTRY *new_entry;
62 
63 	new_entry = g_new(WB_PROJECT_ENTRY, 1);
64 	memset(new_entry, 0, sizeof(*new_entry));
65 	new_entry->status = PROJECT_ENTRY_STATUS_UNKNOWN;
66 
67     return new_entry;
68 }
69 
70 
71 /* Free a workbench entry */
wb_project_entry_free(WB_PROJECT_ENTRY * entry)72 static void wb_project_entry_free(WB_PROJECT_ENTRY *entry)
73 {
74 	wb_project_free(entry->project);
75 	g_free(entry->abs_filename);
76 	g_free(entry->rel_filename);
77 	g_free(entry);
78 }
79 
80 
81 /** Create a new empty workbench.
82  *
83  * @return Address of the new structure.
84  *
85  **/
workbench_new(void)86 WORKBENCH *workbench_new(void)
87 {
88 	WORKBENCH *new_wb;
89 
90 	new_wb = g_new0(WORKBENCH, 1);
91 	memset(new_wb, 0, sizeof(*new_wb));
92 	new_wb->modified = FALSE;
93 	new_wb->rescan_projects_on_open = TRUE;
94 	new_wb->enable_live_update = TRUE;
95 	new_wb->projects = g_ptr_array_new();
96 	new_wb->bookmarks = g_ptr_array_new();
97 	new_wb->monitor = wb_monitor_new();
98 
99 	return new_wb;
100 }
101 
102 
103 /** Free a workbench.
104  *
105  * @param wb The workbench
106  *
107  **/
workbench_free(WORKBENCH * wb)108 void workbench_free(WORKBENCH *wb)
109 {
110 	WB_PROJECT_ENTRY *entry;
111 	guint index;
112 
113 	if (wb == NULL)
114 	{
115 		return;
116 	}
117 
118 	/* Free projects and project entries first */
119 	for (index = 0 ; index < wb->projects->len ; index++)
120 	{
121 		entry = g_ptr_array_index(wb->projects, index);
122 		if (entry != NULL)
123 		{
124 			wb_project_entry_free(entry);
125 		}
126 	}
127 
128 	wb_monitor_free(wb->monitor);
129 	g_ptr_array_free (wb->projects, TRUE);
130 	g_free(wb);
131 }
132 
133 
134 /** Is the workbench empty?
135  *
136  * @param wb The workbench
137  * @return TRUE is workbench is empty or wb == NULL,
138  *         FALSE otherwise.
139  *
140  **/
workbench_is_empty(WORKBENCH * wb)141 gboolean workbench_is_empty(WORKBENCH *wb)
142 {
143 	if (wb != NULL)
144 	{
145 		return (wb->projects->len == 0);
146 	}
147 	return TRUE;
148 }
149 
150 
151 /** Get the project count.
152  *
153  * @param wb The workbench
154  * @return TRUE is workbench is empty or wb == NULL,
155  *         FALSE otherwise.
156  *
157  **/
workbench_get_project_count(WORKBENCH * wb)158 guint workbench_get_project_count(WORKBENCH *wb)
159 {
160 	if (wb != NULL)
161 	{
162 		return wb->projects->len;
163 	}
164 	return 0;
165 }
166 
167 
168 /** Is the workbench modified?
169  *
170  * @param wb The workbench
171  * @return TRUE if the workbench is modified,
172  *         FALSE if not or wb == NULL
173  *
174  **/
workbench_is_modified(WORKBENCH * wb)175 gboolean workbench_is_modified(WORKBENCH *wb)
176 {
177 	if (wb != NULL)
178 	{
179 		return wb->modified;
180 	}
181 	return FALSE;
182 }
183 
184 
185 /** Set the "Rescan projects on open" option.
186  *
187  * @param wb    The workbench
188  * @param value The value to set
189  *
190  **/
workbench_set_rescan_projects_on_open(WORKBENCH * wb,gboolean value)191 void workbench_set_rescan_projects_on_open(WORKBENCH *wb, gboolean value)
192 {
193 	if (wb != NULL)
194 	{
195 		if (wb->rescan_projects_on_open != value)
196 		{
197 			wb->rescan_projects_on_open = value;
198 			wb->modified = TRUE;
199 		}
200 	}
201 }
202 
203 
204 /** Get the "Rescan projects on open" option.
205  *
206  * @param wb The workbench
207  * @return TRUE = rescan all projects after opening the workbench,
208  *         FALSE = don't
209  *
210  **/
workbench_get_rescan_projects_on_open(WORKBENCH * wb)211 gboolean workbench_get_rescan_projects_on_open(WORKBENCH *wb)
212 {
213 	if (wb != NULL)
214 	{
215 		return wb->rescan_projects_on_open;
216 	}
217 	return FALSE;
218 }
219 
220 
221 /** Set the "Enable live update" option.
222  *
223  * @param wb    The workbench
224  * @param value The value to set
225  *
226  **/
workbench_set_enable_live_update(WORKBENCH * wb,gboolean value)227 void workbench_set_enable_live_update(WORKBENCH *wb, gboolean value)
228 {
229 	if (wb != NULL)
230 	{
231 		if (wb->enable_live_update != value)
232 		{
233 			wb->enable_live_update = value;
234 			wb->modified = TRUE;
235 		}
236 	}
237 }
238 
239 
240 /** Get the "Enable live update" option.
241  *
242  * @param wb The workbench
243  * @return TRUE = use file monitoring for live update of the file list,
244  *         FALSE = don't
245  *
246  **/
workbench_get_enable_live_update(WORKBENCH * wb)247 gboolean workbench_get_enable_live_update(WORKBENCH *wb)
248 {
249 	if (wb != NULL)
250 	{
251 		return wb->enable_live_update;
252 	}
253 	return FALSE;
254 }
255 
256 
257 /** Set the "Expand on hover" option.
258  *
259  * @param wb    The workbench
260  * @param value The value to set
261  *
262  **/
workbench_set_expand_on_hover(WORKBENCH * wb,gboolean value)263 void workbench_set_expand_on_hover(WORKBENCH *wb, gboolean value)
264 {
265 	if (wb != NULL)
266 	{
267 		if (wb->expand_on_hover != value)
268 		{
269 			wb->expand_on_hover = value;
270 			wb->modified = TRUE;
271 		}
272 	}
273 }
274 
275 
276 /** Get the "Expand on hover" option.
277  *
278  * @param wb The workbench
279  * @return TRUE = expand a tree-node on hovering over it,
280  *         FALSE = don't
281  *
282  **/
workbench_get_expand_on_hover(WORKBENCH * wb)283 gboolean workbench_get_expand_on_hover(WORKBENCH *wb)
284 {
285 	if (wb != NULL)
286 	{
287 		return wb->expand_on_hover;
288 	}
289 	return FALSE;
290 }
291 
292 
293 /** Set the "Enable Tree Lines" option.
294  *
295  * @param wb    The workbench
296  * @param value The value to set
297  *
298  **/
workbench_set_enable_tree_lines(WORKBENCH * wb,gboolean value)299 void workbench_set_enable_tree_lines(WORKBENCH *wb, gboolean value)
300 {
301 	if (wb != NULL)
302 	{
303 		if (wb->enable_tree_lines != value)
304 		{
305 			wb->enable_tree_lines = value;
306 			wb->modified = TRUE;
307 		}
308 	}
309 }
310 
311 
312 /** Get the "Enable Tree Lines" option.
313  *
314  * @param wb The workbench
315  * @return TRUE = show tree lines,
316  *         FALSE = don't
317  *
318  **/
workbench_get_enable_tree_lines(WORKBENCH * wb)319 gboolean workbench_get_enable_tree_lines(WORKBENCH *wb)
320 {
321 	if (wb != NULL)
322 	{
323 		return wb->enable_tree_lines;
324 	}
325 	return FALSE;
326 }
327 
328 
329 /** Set the filename.
330  *
331  * @param wb       The workbench
332  * @param filename Name of the workbench file
333  *
334  **/
workbench_set_filename(WORKBENCH * wb,const gchar * filename)335 void workbench_set_filename(WORKBENCH *wb, const gchar *filename)
336 {
337 	if (wb != NULL)
338 	{
339 		guint offset;
340 		gchar *ext;
341 
342 		wb->filename = g_strdup(filename);
343 		wb->name = g_path_get_basename (filename);
344 		ext = g_strrstr(wb->name, ".geanywb");
345 		if(ext != NULL)
346 		{
347 			offset = strlen(wb->name);
348 			offset -= strlen(".geanywb");
349 			if (ext == wb->name + offset)
350 			{
351 				/* Strip of file extension by overwriting
352 				   '.' with string terminator. */
353 				wb->name[offset] = '\0';
354 			}
355 		}
356 	}
357 }
358 
359 
360 /** Get the filename.
361  *
362  * @param wb The workbench
363  * @return The filename or NULL
364  *
365  **/
workbench_get_filename(WORKBENCH * wb)366 const gchar *workbench_get_filename(WORKBENCH *wb)
367 {
368 	if (wb != NULL)
369 	{
370 		return wb->filename;
371 	}
372 	return NULL;
373 }
374 
375 
376 /** Get the monitor.
377  *
378  * @param wb The workbench
379  * @return Reference to the WB_MONITOR (can be NULL).
380  *
381  **/
workbench_get_monitor(WORKBENCH * wb)382 WB_MONITOR *workbench_get_monitor(WORKBENCH *wb)
383 {
384 	if (wb != NULL)
385 	{
386 		return wb->monitor;
387 	}
388 	return NULL;
389 }
390 
391 
392 /** Get the name.
393  *
394  * @param wb The workbench
395  * @return The name or NULL
396  *
397  **/
workbench_get_name(WORKBENCH * wb)398 gchar *workbench_get_name(WORKBENCH *wb)
399 {
400 	if (wb != NULL)
401 	{
402 		return wb->name;
403 	}
404 	return NULL;
405 }
406 
407 
408 /** Get the project stored in the workbench at @a index.
409  *
410  * @param wb    The workbench
411  * @param index The index
412  * @return Adress of the WB_PROJECT structure or NULL
413  *         if wb == NULL or an invalid index
414  *
415  **/
workbench_get_project_at_index(WORKBENCH * wb,guint index)416 WB_PROJECT *workbench_get_project_at_index (WORKBENCH *wb, guint index)
417 {
418 	if (wb != NULL)
419 	{
420 		WB_PROJECT_ENTRY *entry;
421 		entry = g_ptr_array_index(wb->projects, index);
422 		if (entry == NULL)
423 		{
424 			return NULL;
425 		}
426 		return entry->project;
427 	}
428 	return NULL;
429 }
430 
431 
432 /** Get the project status in the workbench at @a index.
433  *
434  * Actually the project status just gives information about wheter
435  * the project file for the project at @a index was found or not.
436  *
437  * @param wb    The workbench
438  * @param index The index
439  * @return the status or PROJECT_ENTRY_STATUS_UNKNOWN if wb == NULL
440  *
441  **/
workbench_get_project_status_at_index(WORKBENCH * wb,guint index)442 PROJECT_ENTRY_STATUS workbench_get_project_status_at_index (WORKBENCH *wb, guint index)
443 {
444 	if (wb != NULL)
445 	{
446 		WB_PROJECT_ENTRY *entry;
447 		entry = g_ptr_array_index(wb->projects, index);
448 		if (entry == NULL)
449 		{
450 			return PROJECT_ENTRY_STATUS_UNKNOWN;
451 		}
452 		return entry->status;
453 	}
454 	return PROJECT_ENTRY_STATUS_UNKNOWN;
455 }
456 
457 
458 /** Get the project status in the workbench by address.
459  *
460  * Actually the project status just gives information about wheter
461  * the project file for the project at @a index was found or not.
462  *
463  * @param wb      The workbench
464  * @param address Location of the project
465  * @return the status or PROJECT_ENTRY_STATUS_UNKNOWN if wb == NULL
466  *
467  **/
workbench_get_project_status_by_address(WORKBENCH * wb,WB_PROJECT * address)468 PROJECT_ENTRY_STATUS workbench_get_project_status_by_address (WORKBENCH *wb, WB_PROJECT *address)
469 {
470 	guint index;
471 	if (wb != NULL || address == NULL)
472 	{
473 		WB_PROJECT_ENTRY *entry;
474 		for (index = 0 ; index < wb->projects->len ; index++)
475 		{
476 			entry = g_ptr_array_index(wb->projects, index);
477 			if (entry != NULL && entry->project == address)
478 			{
479 				return entry->status;
480 			}
481 		}
482 	}
483 	return PROJECT_ENTRY_STATUS_UNKNOWN;
484 }
485 
486 
487 /** Add a project to the workbench.
488  *
489  * @param wb       The workbench
490  * @param filename Project file
491  * @return TRUE on success, FALSE otherwise
492  *
493  **/
workbench_add_project(WORKBENCH * wb,const gchar * filename)494 gboolean workbench_add_project(WORKBENCH *wb, const gchar *filename)
495 {
496 	if (wb != NULL)
497 	{
498 		GStatBuf buf;
499 		WB_PROJECT *project;
500 		WB_PROJECT_ENTRY *entry;
501 
502 		entry = wb_project_entry_new();
503 		if (entry == NULL)
504 		{
505 			return FALSE;
506 		}
507 		project = wb_project_new(filename);
508 		if (project == NULL)
509 		{
510 			wb_project_entry_free(entry);
511 			return FALSE;
512 		}
513 
514 		/* Set entry data:
515 		   - absolute and relative filename (relative to workbench file)
516 		   - per default use relative path
517 		   - check status of project file
518 		   - pointer to the project data */
519 		entry->abs_filename = g_strdup(filename);
520 		entry->rel_filename = get_any_relative_path
521 		                          (wb->filename, filename);
522 		entry->use_abs = FALSE;
523 		entry->project = project;
524 		if (g_stat (filename, &buf) == 0)
525 		{
526 			entry->status = PROJECT_ENTRY_STATUS_OK;
527 		}
528 		else
529 		{
530 			entry->status = PROJECT_ENTRY_STATUS_NOT_FOUND;
531 		}
532 		g_ptr_array_add (wb->projects, entry);
533 
534 		/* Load project to import base path. */
535 		wb_project_load(project, filename, NULL);
536 
537 		/* Start immediate scan if enabled. */
538 		if (wb->rescan_projects_on_open == TRUE)
539 		{
540 			wb_project_rescan(project);
541 		}
542 
543 		wb->modified = TRUE;
544 		return TRUE;
545 	}
546 	return FALSE;
547 }
548 
549 
550 /** Remove a project from the workbench.
551  *
552  * @param wb      The workbench
553  * @param project The Project
554  * @return TRUE on success, FALSE otherwise
555  *
556  **/
workbench_remove_project_with_address(WORKBENCH * wb,WB_PROJECT * project)557 gboolean workbench_remove_project_with_address(WORKBENCH *wb, WB_PROJECT *project)
558 {
559 	if (wb != NULL && wb->projects != NULL)
560 	{
561 		guint index;
562 		WB_PROJECT_ENTRY *current;
563 
564 		for (index = 0 ; index < wb->projects->len ; index++)
565 		{
566 			current = g_ptr_array_index(wb->projects, index);
567 			if (current != NULL && current->project == project)
568 			{
569 				g_ptr_array_remove_index (wb->projects, index);
570 				wb_project_entry_free(current);
571 				wb->modified = TRUE;
572 				return TRUE;
573 			}
574 		}
575 	}
576 	return FALSE;
577 }
578 
579 
580 /** Is the file included in the workbench?
581  *
582  * @param wb       The workbench
583  * @param filename The file
584  * @return Address of project in which the file is included.
585  *         NULL if the file is not included in any workbench project.
586  *
587  **/
workbench_file_is_included(WORKBENCH * wb,const gchar * filename)588 WB_PROJECT *workbench_file_is_included (WORKBENCH *wb, const gchar *filename)
589 {
590 	if (wb != NULL)
591 	{
592 		guint index;
593 		WB_PROJECT_ENTRY *current;
594 
595 		for (index = 0 ; index < wb->projects->len ; index++)
596 		{
597 			current = g_ptr_array_index(wb->projects, index);
598 			if (current != NULL && wb_project_file_is_included(current->project, filename) == TRUE)
599 			{
600 				return current->project;
601 			}
602 		}
603 	}
604 	return NULL;
605 }
606 
607 
608 /* Add a workbench bookmark */
workbench_add_bookmark_int(WORKBENCH * wb,const gchar * filename)609 static gboolean workbench_add_bookmark_int(WORKBENCH *wb, const gchar *filename)
610 {
611 	if (wb != NULL && filename != NULL)
612 	{
613 		gchar *new;
614 
615 		new = g_strdup(filename);
616 		g_ptr_array_add (wb->bookmarks, new);
617 		return TRUE;
618 	}
619 	return FALSE;
620 }
621 
622 
623 /** Add a bookmark to a workbench.
624  *
625  * @param wb       The workbench
626  * @param filename File to bookmark
627  * @return TRUE on success, FALSE otherwise
628  *
629  **/
workbench_add_bookmark(WORKBENCH * wb,const gchar * filename)630 gboolean workbench_add_bookmark(WORKBENCH *wb, const gchar *filename)
631 {
632 	if (workbench_add_bookmark_int(wb, filename) == TRUE)
633 	{
634 		wb->modified = TRUE;
635 		return TRUE;
636 	}
637 	return FALSE;
638 }
639 
640 
641 /** Remove a bookmark from a workbench by filename address.
642  *
643  * @param wb       The workbench
644  * @param filename File to remove
645  * @return TRUE on success, FALSE otherwise
646  *
647  **/
workbench_remove_bookmark(WORKBENCH * wb,const gchar * filename)648 gboolean workbench_remove_bookmark(WORKBENCH *wb, const gchar *filename)
649 {
650 	if (wb != NULL)
651 	{
652 		guint index;
653 		gchar *current;
654 
655 		for (index = 0 ; index < wb->bookmarks->len ; index++)
656 		{
657 			current = g_ptr_array_index(wb->bookmarks, index);
658 			if (current == filename)
659 			{
660 				g_ptr_array_remove_index (wb->bookmarks, index);
661 				wb->modified = TRUE;
662 				return TRUE;
663 			}
664 		}
665 	}
666 	return FALSE;
667 }
668 
669 
670 /** Get the bookmark at @a index.
671  *
672  * @param wb    The workbench
673  * @param index The index
674  * @return Address of filename or NULL
675  *
676  **/
workbench_get_bookmark_at_index(WORKBENCH * wb,guint index)677 gchar *workbench_get_bookmark_at_index (WORKBENCH *wb, guint index)
678 {
679 	if (wb != NULL)
680 	{
681 		gchar *file;
682 		file = g_ptr_array_index(wb->bookmarks, index);
683 		if (file == NULL)
684 		{
685 			return NULL;
686 		}
687 		return file;
688 	}
689 	return NULL;
690 }
691 
692 
693 /** Get the number of bookmarks in a workbench.
694  *
695  * @param wb The workbench
696  * @return The number of bookmarks or 0 if wb == NULL
697  *
698  **/
workbench_get_bookmarks_count(WORKBENCH * wb)699 guint workbench_get_bookmarks_count(WORKBENCH *wb)
700 {
701 	if (wb != NULL && wb->bookmarks != NULL)
702 	{
703 		return wb->bookmarks->len;
704 	}
705 	return 0;
706 }
707 
708 
709 /** Save a workbench.
710  *
711  * @param wb    The workbench
712  * @param error Location for returning an GError
713  * @return TRUE on success, FALSE otherwise
714  *
715  **/
workbench_save(WORKBENCH * wb,GError ** error)716 gboolean workbench_save(WORKBENCH *wb, GError **error)
717 {
718 	gboolean success = FALSE;
719 
720 	if (wb != NULL)
721 	{
722 		GKeyFile *kf;
723 		guint	 index;
724 		gchar    *contents;
725 		gchar    group[20];
726 		gsize    length, boomarks_size;
727 		WB_PROJECT_ENTRY *entry;
728 
729 		kf = g_key_file_new ();
730 
731 		/* Save common, simple values */
732 		g_key_file_set_string(kf, "General", "filetype", "workbench");
733 		g_key_file_set_string(kf, "General", "version", "1.0");
734 		g_key_file_set_boolean(kf, "General", "RescanProjectsOnOpen", wb->rescan_projects_on_open);
735 		g_key_file_set_boolean(kf, "General", "EnableLiveUpdate", wb->enable_live_update);
736 		g_key_file_set_boolean(kf, "General", "ExpandOnHover", wb->expand_on_hover);
737 		g_key_file_set_boolean(kf, "General", "EnableTreeLines", wb->enable_tree_lines);
738 
739 		/* Save Workbench bookmarks as string list */
740 		boomarks_size = workbench_get_bookmarks_count(wb);
741 		if (boomarks_size > 0)
742 		{
743 			gchar **bookmarks_strings, *file, *rel_path;
744 
745 			bookmarks_strings = g_new0(gchar *, boomarks_size+1);
746 			for (index = 0 ; index < boomarks_size ; index++ )
747 			{
748 				file = workbench_get_bookmark_at_index(wb, index);
749 				rel_path = get_any_relative_path(wb->filename, file);
750 
751 				bookmarks_strings[index] = rel_path;
752 			}
753 			g_key_file_set_string_list
754 				(kf, "General", "Bookmarks", (const gchar **)bookmarks_strings, boomarks_size);
755 			for (index = 0 ; index < boomarks_size ; index++ )
756 			{
757 				g_free (bookmarks_strings[index]);
758 			}
759 			g_free(bookmarks_strings);
760 		}
761 
762 		/* Save projects data */
763 		for (index = 0 ; index < wb->projects->len ; index++)
764 		{
765 			entry = g_ptr_array_index(wb->projects, index);
766 			g_snprintf(group, sizeof(group), "Project-%u", (index+1));
767 			g_key_file_set_string(kf, group, "AbsFilename", entry->abs_filename);
768 			g_key_file_set_string(kf, group, "RelFilename", entry->rel_filename);
769 			g_key_file_set_boolean(kf, group, "UseAbsFilename", entry->use_abs);
770 		}
771 		contents = g_key_file_to_data (kf, &length, error);
772 		if (contents != NULL && *error == NULL)
773 		{
774 			g_key_file_free(kf);
775 
776 			success = g_file_set_contents (wb->filename, contents, length, error);
777 			if (success)
778 			{
779 				wb->modified = FALSE;
780 			}
781 			g_free (contents);
782 		}
783 	}
784 	else if (error != NULL)
785 	{
786 		g_set_error (error, 0, 0,
787 					 "Internal error: param missing (file: %s, line %d)",
788 					 __FILE__, __LINE__);
789 	}
790 
791 	return success;
792 }
793 
794 
795 /** Load a workbench file.
796  *
797  * The function loads the workbench settings from file @a filename and
798  * stores it into @a wb.
799  *
800  * @param wb       The workbench
801  * @param filename File to load from
802  * @param error    Location for returning an GError
803  * @return TRUE on success, FALSE otherwise
804  *
805  **/
workbench_load(WORKBENCH * wb,const gchar * filename,GError ** error)806 gboolean workbench_load(WORKBENCH *wb, const gchar *filename, GError **error)
807 {
808 	gboolean success = FALSE;
809 
810 	if (wb != NULL)
811 	{
812 		GKeyFile *kf;
813 		gboolean valid = TRUE;
814 		guint    index;
815 		gchar    *contents, **bookmarks_strings;
816 		gchar    group[20];
817 		gsize    length;
818 		WB_PROJECT_ENTRY *entry;
819 
820 		if (!g_file_get_contents (filename, &contents, &length, error))
821 		{
822 			return FALSE;
823 		}
824 
825 		kf = g_key_file_new ();
826 
827 		if (!g_key_file_load_from_data (kf, contents, length,
828 					G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS,
829 					error))
830 		{
831 			g_key_file_free (kf);
832 			g_free (contents);
833 			return FALSE;
834 		}
835 
836 		if (g_key_file_has_key (kf, "General", "filetype", NULL)
837 			&& g_key_file_has_key (kf, "General", "version", NULL))
838 		{
839 			gchar *check;
840 			check = g_key_file_get_string (kf, "General", "filetype", error);
841 			if (check == NULL || g_strcmp0(check, "workbench") != 0)
842 			{
843 				valid = FALSE;
844 			}
845 			g_free(check);
846 		}
847 		else
848 		{
849 			valid = FALSE;
850 		}
851 
852 		if (!valid)
853 		{
854 			g_set_error (error, 0, 0,
855 						 _("File %s is not a valid workbench file!"),
856 						 filename);
857 			return FALSE;
858 		}
859 		workbench_set_filename(wb, filename);
860 		wb->rescan_projects_on_open = g_key_file_get_boolean(kf, "General", "RescanProjectsOnOpen", error);
861 		if (g_key_file_has_key (kf, "General", "EnableLiveUpdate", error))
862 		{
863 			wb->enable_live_update = g_key_file_get_boolean(kf, "General", "EnableLiveUpdate", error);
864 		}
865 		else
866 		{
867 			/* Not found. Might happen if the workbench was created with an older version of the plugin.
868 			   Initialize with TRUE. */
869 			wb->enable_live_update = TRUE;
870 		}
871 		if (g_key_file_has_key (kf, "General", "ExpandOnHover", error))
872 		{
873 			wb->expand_on_hover = g_key_file_get_boolean(kf, "General", "ExpandOnHover", error);
874 		}
875 		else
876 		{
877 			/* Not found. Might happen if the workbench was created with an older version of the plugin.
878 			   Initialize with FALSE. */
879 			wb->expand_on_hover = FALSE;
880 		}
881 		if (g_key_file_has_key (kf, "General", "EnableTreeLines", error))
882 		{
883 			wb->enable_tree_lines = g_key_file_get_boolean(kf, "General", "EnableTreeLines", error);
884 		}
885 		else
886 		{
887 			/* Not found. Might happen if the workbench was created with an older version of the plugin.
888 			   Initialize with FALSE. */
889 			wb->enable_tree_lines = FALSE;
890 		}
891 
892 		/* Load Workbench bookmarks from string list */
893 		bookmarks_strings = g_key_file_get_string_list (kf, "General", "Bookmarks", NULL, error);
894 		if (bookmarks_strings != NULL)
895 		{
896 			gchar **file, *abs_path;
897 
898 			file = bookmarks_strings;
899 			while (*file != NULL)
900 			{
901 				abs_path = get_combined_path(wb->filename, *file);
902 				if (abs_path != NULL)
903 				{
904 					workbench_add_bookmark_int(wb, abs_path);
905 					g_free(abs_path);
906 				}
907 				file++;
908 			}
909 			g_strfreev(bookmarks_strings);
910 		}
911 
912 		/* Load projects data */
913 		for (index = 0 ; index < 1024 ; index++)
914 		{
915 			g_snprintf(group, sizeof(group), "Project-%u", (index+1));
916 			if (g_key_file_has_key (kf, group, "AbsFilename", NULL))
917 			{
918 				gchar *prj_filename;
919 				entry = wb_project_entry_new();
920 				if (entry == NULL)
921 				{
922 					continue;
923 				}
924 				entry->abs_filename = g_key_file_get_string(kf, group, "AbsFilename", error);
925 				entry->rel_filename = g_key_file_get_string(kf, group, "RelFilename", error);
926 				entry->use_abs = g_key_file_get_boolean(kf, group, "UseAbsFilename", error);
927 				if (entry->use_abs == TRUE)
928 				{
929 					prj_filename = entry->abs_filename;
930 				}
931 				else
932 				{
933 					prj_filename = get_combined_path
934 										(wb->filename, entry->rel_filename);
935 				}
936 				if (prj_filename != NULL)
937 				{
938 					GStatBuf buf;
939 
940 					entry->project = wb_project_new(prj_filename);
941 					if (g_stat (prj_filename, &buf) == 0)
942 					{
943 						entry->status = PROJECT_ENTRY_STATUS_OK;
944 
945 						/* ToDo: collect and handle project load errors */
946 						wb_project_load(entry->project, prj_filename, error);
947 					}
948 					else
949 					{
950 						entry->status = PROJECT_ENTRY_STATUS_NOT_FOUND;
951 					}
952 					g_ptr_array_add (wb->projects, entry);
953 
954 					if (wb->rescan_projects_on_open == TRUE)
955 					{
956 						wb_project_rescan(entry->project);
957 					}
958 				}
959 			}
960 			else
961 			{
962 				break;
963 			}
964 		}
965 
966 		g_key_file_free(kf);
967 		g_free (contents);
968 		success = TRUE;
969 	}
970 	else if (error != NULL)
971 	{
972 		g_set_error (error, 0, 0,
973 						"Internal error: param missing (file: %s, line %d)",
974 						__FILE__, __LINE__);
975 	}
976 
977 	return success;
978 }
979 
980 
981 /* Check if the given pointers are still valid references. */
workbench_references_are_valid(WORKBENCH * wb,WB_PROJECT * prj,WB_PROJECT_DIR * dir)982 static gboolean workbench_references_are_valid(WORKBENCH *wb, WB_PROJECT *prj, WB_PROJECT_DIR *dir)
983 {
984 	guint index;
985 	WB_PROJECT_ENTRY *entry;
986 
987 	if (wb == NULL)
988 	{
989 		return FALSE;
990 	}
991 
992 	/* Try to find the project. */
993 	for (index = 0 ; index < wb->projects->len ; index++)
994 	{
995 		entry = g_ptr_array_index(wb->projects, index);
996 		if (((WB_PROJECT_ENTRY *)entry)->project == prj)
997 		{
998 			break;
999 		}
1000 	}
1001 	if (index >= wb->projects->len)
1002 	{
1003 		return FALSE;
1004 	}
1005 
1006 	/* Project exists in this workbench, let the project validate
1007 	   the directory. */
1008 	return wb_project_is_valid_dir_reference(prj, dir);
1009 }
1010 
1011 
1012 /** Process the add file event.
1013  *
1014  * The function processes the add file event. The pointers are checked for
1015  * validity and on success the task is passed on to the project dir. file can
1016  * be the path of a regular file or a directory.
1017  *
1018  * @param wb   The workbench
1019  * @param prj  The project
1020  * @param dir  The directory
1021  * @param file The new file to add to project/directory
1022  *
1023  **/
workbench_process_add_file_event(WORKBENCH * wb,WB_PROJECT * prj,WB_PROJECT_DIR * dir,const gchar * file)1024 void workbench_process_add_file_event(WORKBENCH *wb, WB_PROJECT *prj, WB_PROJECT_DIR *dir, const gchar *file)
1025 {
1026 	if (workbench_references_are_valid(wb, prj, dir) == FALSE)
1027 	{
1028 		/* Should not happen, log a message and return. */
1029 		g_message("%s: invalid references: wb: %p, prj: %p, dir: %p",
1030 			G_STRFUNC, wb, prj, dir);
1031 		return;
1032 	}
1033 
1034 	wb_project_dir_add_file(prj, dir, file);
1035 }
1036 
1037 
1038 /** Process the remove file event.
1039  *
1040  * The function processes the remove file event. The pointers are checked for
1041  * validity and on success the task is passed on to the project dir. file can
1042  * be the path of a regular file or a directory.
1043  *
1044  * @param wb   The workbench
1045  * @param prj  The project
1046  * @param dir  The directory
1047  * @param file The file to remove from project/directory
1048  *
1049  **/
workbench_process_remove_file_event(WORKBENCH * wb,WB_PROJECT * prj,WB_PROJECT_DIR * dir,const gchar * file)1050 void workbench_process_remove_file_event(WORKBENCH *wb, WB_PROJECT *prj, WB_PROJECT_DIR *dir, const gchar *file)
1051 {
1052 	if (workbench_references_are_valid(wb, prj, dir) == FALSE)
1053 	{
1054 		/* Should not happen, log a message and return. */
1055 		g_message("%s: invalid references: wb: %p, prj: %p, dir: %p",
1056 			G_STRFUNC, wb, prj, dir);
1057 		return;
1058 	}
1059 
1060 	wb_project_dir_remove_file(prj, dir, file);
1061 }
1062 
1063 
1064 /* Foreach callback function for creating file monitors. */
workbench_enable_live_update_foreach_cb(SIDEBAR_CONTEXT * context,gpointer userdata)1065 static void workbench_enable_live_update_foreach_cb(SIDEBAR_CONTEXT *context,
1066 													gpointer userdata)
1067 {
1068 	gchar *dirpath = NULL;
1069 	gchar *abs_path = NULL;
1070 	WB_MONITOR *monitor;
1071 
1072 	if (context->project != NULL && context->directory != NULL)
1073 	{
1074 		if (context->subdir != NULL)
1075 		{
1076 			dirpath = context->subdir;
1077 		}
1078 		else
1079 		{
1080 			abs_path = get_combined_path(wb_project_get_filename(context->project),
1081 										wb_project_dir_get_base_dir(context->directory));
1082 			dirpath = abs_path;
1083 		}
1084 	}
1085 
1086 	if (dirpath != NULL)
1087 	{
1088 		monitor = userdata;
1089 		wb_monitor_add_dir(monitor, context->project, context->directory, dirpath);
1090 	}
1091 
1092 	g_free(abs_path);
1093 }
1094 
1095 
1096 /** Enable live update.
1097  *
1098  * The function enables live update of the workbench by creating file
1099  * monitors for all directories in the sidebars file tree.
1100  *
1101  * @param wb       The workbench
1102  *
1103  **/
workbench_enable_live_update(WORKBENCH * wb)1104 void workbench_enable_live_update(WORKBENCH *wb)
1105 {
1106 	if (wb != NULL)
1107 	{
1108 		sidebar_call_foreach(DATA_ID_DIRECTORY,
1109 			workbench_enable_live_update_foreach_cb, wb->monitor);
1110 		sidebar_call_foreach(DATA_ID_SUB_DIRECTORY,
1111 			workbench_enable_live_update_foreach_cb, wb->monitor);
1112 	}
1113 }
1114 
1115 
1116 /** Disable live update.
1117  *
1118  * The function disables live update of the workbench by freeing all
1119  * file monitors.
1120  *
1121  * @param wb       The workbench
1122  *
1123  **/
workbench_disable_live_update(WORKBENCH * wb)1124 void workbench_disable_live_update(WORKBENCH *wb)
1125 {
1126 	if (wb != NULL)
1127 	{
1128 		wb_monitor_free(wb->monitor);
1129 	}
1130 }
1131