1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*
3  * anjuta
4  * Copyright (C) James Liggett 2008 <jrliggett@cox.net>
5  *
6  * anjuta is free software.
7  *
8  * You may redistribute it and/or modify it under the terms of the
9  * GNU General Public License, as published by the Free Software
10  * Foundation; either version 2 of the License, or (at your option)
11  * any later version.
12  *
13  * anjuta is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16  * See the GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with anjuta.  If not, write to:
20  * 	The Free Software Foundation, Inc.,
21  * 	51 Franklin Street, Fifth Floor
22  * 	Boston, MA  02110-1301, USA.
23  */
24 
25 #include "git-status-command.h"
26 
27 struct _GitStatusCommandPriv
28 {
29 	GQueue *status_queue;
30 	GitStatusFactory *factory;
31 	GFileMonitor *head_monitor;
32 	GFileMonitor *index_monitor;
33 };
34 
35 G_DEFINE_TYPE (GitStatusCommand, git_status_command, GIT_TYPE_COMMAND);
36 
37 static guint
git_status_command_run(AnjutaCommand * command)38 git_status_command_run (AnjutaCommand *command)
39 {
40 	git_command_add_arg (GIT_COMMAND (command), "status");
41 	git_command_add_arg (GIT_COMMAND (command), "--porcelain");
42 
43 	return 0;
44 }
45 
46 static void
git_status_command_handle_output(GitCommand * git_command,const gchar * output)47 git_status_command_handle_output (GitCommand *git_command, const gchar *output)
48 {
49 	GitStatusCommand *self;
50 	GitStatus *status_object;
51 
52 	self = GIT_STATUS_COMMAND (git_command);
53 	status_object = git_status_factory_create_status (self->priv->factory,
54 	                                                  output);
55 	if (status_object)
56 	{
57 		g_queue_push_tail (self->priv->status_queue, status_object);
58 		anjuta_command_notify_data_arrived (ANJUTA_COMMAND (self));
59 	}
60 }
61 
62 static void
git_status_command_init(GitStatusCommand * self)63 git_status_command_init (GitStatusCommand *self)
64 {
65 	self->priv = g_new0 (GitStatusCommandPriv, 1);
66 	self->priv->status_queue = g_queue_new ();
67 	self->priv->factory = git_status_factory_new ();
68 }
69 
70 static void
on_file_monitor_changed(GFileMonitor * monitor,GFile * file,GFile * other_file,GFileMonitorEvent event,AnjutaCommand * command)71 on_file_monitor_changed (GFileMonitor *monitor, GFile *file, GFile *other_file,
72                          GFileMonitorEvent event, AnjutaCommand *command)
73 {
74 	/* Handle created and modified events just to cover all possible cases.
75 	 * Sometimes git does some odd things... */
76 	if (event == G_FILE_MONITOR_EVENT_CHANGED ||
77 	    event == G_FILE_MONITOR_EVENT_CREATED)
78 	{
79 		anjuta_command_start (command);
80 	}
81 }
82 
83 static gboolean
git_status_command_start_automatic_monitor(AnjutaCommand * command)84 git_status_command_start_automatic_monitor (AnjutaCommand *command)
85 {
86 	GitStatusCommand *self;
87 	gchar *working_directory;
88 	gchar *git_head_path;
89 	gchar *git_index_path;
90 	GFile *git_head_file;
91 	GFile *git_index_file;
92 
93 	self = GIT_STATUS_COMMAND (command);
94 
95 	g_object_get (self, "working-directory", &working_directory, NULL);
96 
97 	/* Watch for changes to the HEAD file and the index file, so that we can
98 	 * at least detect commits and index changes. */
99 	git_head_path = g_strjoin (G_DIR_SEPARATOR_S,
100 	                           working_directory,
101 	                           ".git",
102 	                           "HEAD",
103 	                           NULL);
104 	git_index_path = g_strjoin (G_DIR_SEPARATOR_S,
105 	                            working_directory,
106 	                            ".git",
107 	                            "index",
108 	                            NULL);
109 	git_head_file = g_file_new_for_path (git_head_path);
110 	git_index_file = g_file_new_for_path (git_index_path);
111 	self->priv->head_monitor = g_file_monitor_file (git_head_file, 0, NULL,
112 	                                                NULL);
113 	self->priv->index_monitor = g_file_monitor_file (git_index_file, 0, NULL,
114 	                                                 NULL);
115 
116 	g_signal_connect (G_OBJECT (self->priv->head_monitor), "changed",
117 	                  G_CALLBACK (on_file_monitor_changed),
118 	                  command);
119 
120 	g_signal_connect (G_OBJECT (self->priv->index_monitor), "changed",
121 	                  G_CALLBACK (on_file_monitor_changed),
122 	                  command);
123 
124 	g_free (git_head_path);
125 	g_free (git_index_path);
126 	g_object_unref (git_head_file);
127 	g_object_unref (git_index_file);
128 
129 	return TRUE;
130 }
131 
132 static void
git_status_command_stop_automatic_monitor(AnjutaCommand * command)133 git_status_command_stop_automatic_monitor (AnjutaCommand *command)
134 {
135 	GitStatusCommand *self;
136 
137 	self = GIT_STATUS_COMMAND (command);
138 
139 	if (self->priv->head_monitor)
140 	{
141 		g_file_monitor_cancel (self->priv->head_monitor);
142 		g_object_unref (self->priv->head_monitor);
143 		self->priv->head_monitor = NULL;
144 	}
145 
146 	if (self->priv->index_monitor)
147 	{
148 		g_file_monitor_cancel (self->priv->index_monitor);
149 		g_object_unref (self->priv->index_monitor);
150 		self->priv->index_monitor = NULL;
151 	}
152 }
153 
154 static void
git_status_command_clear_output(GitStatusCommand * self)155 git_status_command_clear_output (GitStatusCommand *self)
156 {
157 	GList *current_output;
158 
159 	current_output = self->priv->status_queue->head;
160 
161 	while (current_output)
162 	{
163 		g_object_unref (current_output->data);
164 		current_output = g_list_next (current_output);
165 	}
166 
167 	g_queue_clear (self->priv->status_queue);
168 }
169 
170 static void
git_status_command_data_arrived(AnjutaCommand * command)171 git_status_command_data_arrived (AnjutaCommand *command)
172 {
173 	git_status_command_clear_output (GIT_STATUS_COMMAND (command));
174 }
175 
176 
177 static void
git_status_command_finalize(GObject * object)178 git_status_command_finalize (GObject *object)
179 {
180 	GitStatusCommand *self;
181 
182 	self = GIT_STATUS_COMMAND (object);
183 
184 	git_status_command_clear_output (self);
185 	git_status_command_stop_automatic_monitor (ANJUTA_COMMAND (self));
186 
187 	g_queue_free (self->priv->status_queue);
188 	g_object_unref (self->priv->factory);
189 
190 	g_free (self->priv);
191 
192 	G_OBJECT_CLASS (git_status_command_parent_class)->finalize (object);
193 }
194 
195 static void
git_status_command_class_init(GitStatusCommandClass * klass)196 git_status_command_class_init (GitStatusCommandClass *klass)
197 {
198 	GObjectClass* object_class = G_OBJECT_CLASS (klass);
199 	GitCommandClass* parent_class = GIT_COMMAND_CLASS (klass);
200 	AnjutaCommandClass* command_class = ANJUTA_COMMAND_CLASS (klass);
201 
202 	object_class->finalize = git_status_command_finalize;
203 	parent_class->output_handler = git_status_command_handle_output;
204 	command_class->run = git_status_command_run;
205 	command_class->data_arrived = git_status_command_data_arrived;
206 	command_class->start_automatic_monitor = git_status_command_start_automatic_monitor;
207 	command_class->stop_automatic_monitor = git_status_command_stop_automatic_monitor;
208 }
209 
210 
211 GitStatusCommand *
git_status_command_new(const gchar * working_directory)212 git_status_command_new (const gchar *working_directory)
213 {
214 	GitStatusCommand *self;
215 
216 	self = g_object_new (GIT_TYPE_STATUS_COMMAND,
217 						 "working-directory", working_directory,
218 						 "single-line-output", TRUE,
219 						 NULL);
220 
221 	return self;
222 }
223 
224 GQueue *
git_status_command_get_status_queue(GitStatusCommand * self)225 git_status_command_get_status_queue (GitStatusCommand *self)
226 {
227 	return self->priv->status_queue;
228 }
229