1 /*
2  * Copyright 2018 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 file monitoring.
21  */
22 #include <glib/gstdio.h>
23 
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
27 
28 #include <geanyplugin.h>
29 #include "wb_globals.h"
30 #include "workbench.h"
31 #include "wb_monitor.h"
32 #include "utils.h"
33 
34 
35 struct S_WB_MONITOR
36 {
37 	GHashTable *monitors;
38 };
39 
40 typedef struct
41 {
42 	GFileMonitor *monitor;
43 	WB_PROJECT *prj;
44 	WB_PROJECT_DIR *dir;
45 }WB_MONITOR_ENTRY;
46 
47 
48 /** Create a new, empty WB_MONITOR.
49  *
50  * @return Address of the new WB_MONITOR
51  *
52  **/
wb_monitor_new(void)53 WB_MONITOR *wb_monitor_new(void)
54 {
55 	WB_MONITOR *monitor;
56 
57 	monitor = g_new0(WB_MONITOR, 1);
58 
59 	return monitor;
60 }
61 
62 
63 /* Create a new monitor entry */
wb_monitor_entry_new(GFileMonitor * monitor,WB_PROJECT * prj,WB_PROJECT_DIR * dir)64 static WB_MONITOR_ENTRY *wb_monitor_entry_new (GFileMonitor *monitor,
65 							WB_PROJECT *prj, WB_PROJECT_DIR *dir)
66 {
67 	WB_MONITOR_ENTRY *new;
68 
69 	new =  g_new0(WB_MONITOR_ENTRY, 1);
70 	new->monitor = monitor;
71 	new->prj = prj;
72 	new->dir = dir;
73 
74 	return new;
75 }
76 
77 
78 /* Free a monitor entry */
wb_monitor_entry_free(gpointer data)79 static void wb_monitor_entry_free (gpointer data)
80 {
81 	WB_MONITOR_ENTRY *entry = data;
82 
83 	if (data != NULL)
84 	{
85 		g_object_unref(entry->monitor);
86 		g_free(entry);
87 	}
88 }
89 
90 
91 /* Callback function for file monitoring. */
wb_monitor_file_changed_cb(G_GNUC_UNUSED GFileMonitor * monitor,G_GNUC_UNUSED GFile * file,G_GNUC_UNUSED GFile * other_file,GFileMonitorEvent event,WB_MONITOR_ENTRY * entry)92 static void wb_monitor_file_changed_cb(G_GNUC_UNUSED GFileMonitor *monitor,
93 									   G_GNUC_UNUSED GFile *file,
94 									   G_GNUC_UNUSED GFile *other_file,
95 									   GFileMonitorEvent event,
96 									   WB_MONITOR_ENTRY *entry)
97 {
98 	const gchar *event_string = NULL;
99 	gchar *file_path, *other_file_path = NULL;
100 
101 	g_return_if_fail(entry != NULL);
102 
103 	g_message("%s: event: %d", G_STRFUNC, event);
104 
105 	file_path = g_file_get_path (file);
106 	if (other_file != NULL)
107 	{
108 		other_file_path = g_file_get_path (other_file);
109 	}
110 	switch (event)
111 	{
112 		case G_FILE_MONITOR_EVENT_CREATED:
113 			event_string = "FILE_CREATED";
114 			workbench_process_add_file_event (wb_globals.opened_wb,
115 				entry->prj, entry->dir, file_path);
116 			break;
117 
118 		case G_FILE_MONITOR_EVENT_DELETED:
119 			event_string = "FILE_DELETED";
120 			workbench_process_remove_file_event (wb_globals.opened_wb,
121 				entry->prj, entry->dir, file_path);
122 			break;
123 
124 		default:
125 			break;
126 	}
127 
128 	if (event_string != NULL)
129 	{
130 		g_message("%s: Prj: \"%s\" Dir: \"%s\" %s: \"%s\"", G_STRFUNC, wb_project_get_name(entry->prj),
131 			wb_project_dir_get_name(entry->dir), event_string, file_path);
132 	}
133 
134 	g_free(file_path);
135 	g_free(other_file_path);
136 }
137 
138 
139 /** Add a new file monitor
140  *
141  * Add a new file monitor for dirpath. The monitor will only be created
142  * if the settings option "Enable live monitor" is set to on and if
143  * no file monitor exists for dirpath already. If monitor creation fails,
144  * that means g_file_monitor_directory returns NULL, then a message is
145  * output on the statusbar.
146  *
147  * @param monitor The global monitor management
148  * @param prj     The project to which dirpath belongs
149  * @param dir     The directory (WB_PROJECT_DIR) to which dirpath belongs
150  * @param dirpath The path of the directory
151  *
152  **/
wb_monitor_add_dir(WB_MONITOR * monitor,WB_PROJECT * prj,WB_PROJECT_DIR * dir,const gchar * dirpath)153 void wb_monitor_add_dir(WB_MONITOR *monitor, WB_PROJECT *prj,
154 						WB_PROJECT_DIR *dir, const gchar *dirpath)
155 {
156 	GFileMonitor *newmon;
157 	GFile *file;
158 	GError *error = NULL;
159 	WB_MONITOR_ENTRY *entry;
160 
161 	g_return_if_fail(monitor != NULL);
162 	g_return_if_fail(dir != NULL);
163 	g_return_if_fail(dirpath != NULL);
164 
165 	if (workbench_get_enable_live_update(wb_globals.opened_wb) == FALSE)
166 	{
167 		/* Return if the feature is disabled. */
168 		return;
169 	}
170 
171 	if (monitor->monitors == NULL)
172 	{
173 		monitor->monitors = g_hash_table_new_full
174 			(g_str_hash, g_str_equal, g_free, wb_monitor_entry_free);
175 	}
176 	if (g_hash_table_contains(monitor->monitors, dirpath))
177 	{
178 		/* A monitor for that path already exists,
179 		   do not create another one. */
180 		return;
181 	}
182 
183 	/* Setup file monitor for directory */
184 	file = g_file_new_for_path(dirpath);
185 	newmon = g_file_monitor_directory
186 		(file, G_FILE_MONITOR_NONE, NULL, &error);
187 	if (newmon == NULL)
188 	{
189 		/* Create monitor failed. Report error. */
190 		ui_set_statusbar(TRUE,
191 			_("Could not setup file monitoring for directory: \"%s\". Error: %s"),
192 			dirpath, error->message);
193 		g_error_free (error);
194 		return;
195 	}
196 	else
197 	{
198 		/* Add file monitor to hash table. */
199 		entry = wb_monitor_entry_new(newmon, prj, dir);
200 		g_hash_table_insert(monitor->monitors, (gpointer)g_strdup(dirpath), entry);
201 
202 		g_signal_connect(newmon, "changed",
203 			G_CALLBACK(wb_monitor_file_changed_cb), entry);
204 
205 		/* ToDo: make rate limit configurable */
206 		g_file_monitor_set_rate_limit(newmon, 5 * 1000);
207 	}
208 	g_object_unref(file);
209 }
210 
211 
212 /** Remove a file monitor
213  *
214  * Remove the file monitor for dirpath.
215  *
216  * @param monitor The global monitor management
217  * @param dirpath The path of the directory
218  *
219  **/
wb_monitor_remove_dir(WB_MONITOR * monitor,const gchar * dirpath)220 gboolean wb_monitor_remove_dir(WB_MONITOR *monitor, const gchar *dirpath)
221 {
222 	if (monitor == NULL || dirpath == NULL)
223 	{
224 		return FALSE;
225 	}
226 
227 	/* Free the entry. The hash table will call the destroy function
228 	   wb_monitor_entry_free which is doing the work for us. */
229 	return g_hash_table_remove(monitor->monitors, dirpath);
230 }
231 
232 
233 /** Free monitor management/all file monitors.
234  *
235  * @param monitor The global monitor management
236  *
237  **/
wb_monitor_free(WB_MONITOR * monitor)238 void wb_monitor_free(WB_MONITOR *monitor)
239 {
240 	if (monitor != NULL)
241 	{
242 		if (monitor->monitors != NULL)
243 		{
244 			g_hash_table_unref(monitor->monitors);
245 			monitor->monitors = NULL;
246 		}
247 	}
248 }
249