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