1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*
3  * git-shell-test
4  * Copyright (C) James Liggett 2009 <jrliggett@cox.net>
5  *
6  * git-shell-test is free software: you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by the
8  * Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * git-shell-test is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14  * See the GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include "anjuta-dock.h"
21 
22 /**
23  * SECTION: AnjutaDock
24  * @short_description: Docking system for context-driven user interfaces.
25  * @see_also: #AnjutaCommandBar
26  * @include; libanjuta/anjuta-dock.h
27  *
28  * AnjutaDock provides an alternative to the traditional menu and toolbar based
29  * methodologies used by most GNOME programs. Instead, it focuses on tasks, or
30  * related sets of tasks. Each pane in the dock corresponds to a different set
31  * of related tasks.
32  *
33  * Optionally, you can also associate an #AnjutaCommandBar with an AnjutaDock to
34  * provide contextually appropriate sets of commands depending on the currently
35  * visible pane
36  */
37 
38 struct _AnjutaDockPriv
39 {
40 	GHashTable *panes;
41 	GHashTable *dock_items;
42 	GtkWidget *command_bar;
43 
44 	AnjutaDockPane* command_pane;
45 };
46 
47 G_DEFINE_TYPE (AnjutaDock, anjuta_dock, GDL_TYPE_DOCK);
48 
49 static void
anjuta_dock_init(AnjutaDock * self)50 anjuta_dock_init (AnjutaDock *self)
51 {
52 	self->priv = g_new0 (AnjutaDockPriv, 1);
53 	self->priv->panes = g_hash_table_new_full (g_str_hash, g_str_equal,
54 	                                           NULL, g_object_unref);
55 	self->priv->dock_items = g_hash_table_new (g_str_hash, g_str_equal);
56 	self->priv->command_pane = 0;
57 }
58 
59 static void
anjuta_dock_finalize(GObject * object)60 anjuta_dock_finalize (GObject *object)
61 {
62 	AnjutaDock *self;
63 
64 	self = ANJUTA_DOCK (object);
65 
66 	g_hash_table_destroy (self->priv->panes);
67 	g_hash_table_destroy (self->priv->dock_items);
68 	g_free (self->priv);
69 
70 	G_OBJECT_CLASS (anjuta_dock_parent_class)->finalize (object);
71 }
72 
73 static void
on_pane_selected(GdlDockItem * item,AnjutaCommandBar * command_bar)74 on_pane_selected (GdlDockItem *item, AnjutaCommandBar *command_bar)
75 {
76 	const gchar *pane_name;
77 
78 	pane_name = (const gchar *) g_object_get_data (G_OBJECT (item),
79 	                                               "pane-name");
80 	anjuta_command_bar_show_action_group (command_bar, pane_name);
81 }
82 
83 
84 static void
disconnect_select_signals(gpointer key,GdlDockItem * value,AnjutaCommandBar * command_bar)85 disconnect_select_signals (gpointer key, GdlDockItem *value,
86                            AnjutaCommandBar *command_bar)
87 {
88 	g_signal_handlers_disconnect_by_func (value, G_CALLBACK (on_pane_selected),
89 	                                      command_bar);
90 }
91 
92 static void
anjuta_dock_dispose(GObject * object)93 anjuta_dock_dispose (GObject *object)
94 {
95 	AnjutaDock *self;
96 
97 	self = ANJUTA_DOCK (object);
98 
99 	if (self->priv->command_bar)
100 	{
101 		/* Disconnect pane selection signals before dropping the command bar
102 		 * reference. It is possible that we may be holding the last reference
103 		 * of the bar, creating the possibility that the parent implementation
104 		 * of dispose might do something that would call the signal handler that
105 		 * would reference the already destroyed command bar. */
106 		g_hash_table_foreach (self->priv->dock_items,
107 		                      (GHFunc) disconnect_select_signals,
108 		                      self->priv->command_bar);
109 
110 		g_object_unref (self->priv->command_bar);
111 		self->priv->command_bar = NULL;
112 	}
113 
114 	G_OBJECT_CLASS (anjuta_dock_parent_class)->dispose (object);
115 }
116 
117 static void
anjuta_dock_class_init(AnjutaDockClass * klass)118 anjuta_dock_class_init (AnjutaDockClass *klass)
119 {
120 	GObjectClass* object_class = G_OBJECT_CLASS (klass);
121 
122 	object_class->finalize = anjuta_dock_finalize;
123 	object_class->dispose = anjuta_dock_dispose;
124 }
125 
126 /**
127  * anjuta_dock_new:
128  *
129  * Creates a new AnjutaDock.
130  */
131 GtkWidget *
anjuta_dock_new(void)132 anjuta_dock_new (void)
133 {
134 	return g_object_new (ANJUTA_TYPE_DOCK, NULL);
135 }
136 
137 /**
138  * anjuta_dock_add_pane:
139  * @self: An AnjutaDock
140  * @pane_name: A unique name for this pane
141  * @pane_label: Label to display in this pane's grip
142  * @pane: The #AnjutaDockPane to add to the dock. The dock takes ownership of
143  *		  the pane object.
144  * @stock_icon: Stock icon to display in this pane's grip
145  * @placement: A #GdlDockPlacement value indicating where the pane should be
146  *			   placed
147  * @entries: (allow-none) (array length=num_entries): #AnjutaCommandBar entries
148  *			 for this pane. Can be %NULL
149  * @num_entries: The number of entries pointed to by entries, or 0.
150  * @user_data: User data to pass to the entry callback
151  *
152  * Adds a pane, with optional #AnjutaCommandBar entries, to an AnjutaDock. This
153  * method adds a pane with no grip that cannot be closed, floating or iconified.
154  *
155  * Returns: %TRUE if the pane was added, or %FALSE if a pane with the same name
156  * already exists in the dock
157  */
158 gboolean
anjuta_dock_add_pane(AnjutaDock * self,const gchar * pane_name,const gchar * pane_label,const gchar * stock_icon,AnjutaDockPane * pane,GdlDockPlacement placement,AnjutaCommandBarEntry * entries,int num_entries,gpointer user_data)159 anjuta_dock_add_pane (AnjutaDock *self, const gchar *pane_name,
160                       const gchar *pane_label, const gchar *stock_icon,
161                       AnjutaDockPane *pane, GdlDockPlacement placement,
162                       AnjutaCommandBarEntry *entries, int num_entries,
163                       gpointer user_data)
164 {
165 	int behavior;
166 
167 	behavior = 0;
168 	behavior |= GDL_DOCK_ITEM_BEH_NO_GRIP;
169 	behavior |= GDL_DOCK_ITEM_BEH_CANT_CLOSE;
170 	behavior |= GDL_DOCK_ITEM_BEH_CANT_ICONIFY;
171 	behavior |= GDL_DOCK_ITEM_BEH_NEVER_FLOATING;
172 
173 	return anjuta_dock_add_pane_full (self, pane_name, pane_label, stock_icon,
174 	                                  pane, placement, entries, num_entries,
175 	                                  user_data, behavior);
176 }
177 
178 /**
179  * anjuta_dock_add_pane_full:
180  * @self: An AnjutaDock
181  * @pane_name: A unique name for this pane
182  * @pane_label: Label to display in this pane's grip
183  * @stock_icon: Stock icon to display in this pane's grip
184  * @pane: The #AnjutaDockPane to add to the dock. The dock takes ownership of
185  *		  the pane object.
186  * @placement: A #GdlDockPlacement value indicating where the pane should be
187  *			   placed
188  * @entries: (allow-none) (array length=num_entries): #AnjutaCommandBar entries
189  *			 for this pane. Can be %NULL
190  * @num_entries: The number of entries pointed to by entries, or 0.
191  * @user_data: User data to pass to the entry callback
192  * @behavior: Any combination of #GdlDockItemBehavior flags
193  *
194  * Does the same thing as anjuta_dock_add_pane, but allows GDL dock behavior
195  * flags to be specified.
196  *
197  * Returns: %TRUE if the pane was added, or %FALSE if a pane with the same name
198  * already exists in the dock
199  */
200 gboolean
anjuta_dock_add_pane_full(AnjutaDock * self,const gchar * pane_name,const gchar * pane_label,const gchar * stock_icon,AnjutaDockPane * pane,GdlDockPlacement placement,AnjutaCommandBarEntry * entries,int num_entries,gpointer user_data,GdlDockItemBehavior behavior)201 anjuta_dock_add_pane_full (AnjutaDock *self, const gchar *pane_name,
202                            const gchar *pane_label, const gchar *stock_icon,
203                            AnjutaDockPane *pane,
204                            GdlDockPlacement placement,
205                            AnjutaCommandBarEntry *entries, int num_entries,
206                            gpointer user_data,
207                            GdlDockItemBehavior behavior)
208 {
209 	GtkWidget *dock_item;
210 	GtkWidget *child;
211 
212 	dock_item = gdl_dock_item_new (pane_name, pane_label, behavior);
213 	child = anjuta_dock_pane_get_widget (pane);
214 	g_object_set_data (G_OBJECT (child), "dock-item", dock_item);
215 
216 	/* Make sure there isn't another dock with the same name */
217 	if (!g_hash_table_lookup_extended (self->priv->panes, pane_name, NULL,
218 	                                   NULL))
219 	{
220 		/* Take ownership of the pane object */
221 		g_hash_table_insert (self->priv->panes, (gchar *) pane_name, pane);
222 
223 		gtk_container_add (GTK_CONTAINER (dock_item), child);
224 		gdl_dock_add_item (GDL_DOCK (self), GDL_DOCK_ITEM (dock_item), placement);
225 
226 		g_object_set_data (G_OBJECT (dock_item), "pane-name", (gchar *) pane_name);
227 
228 		/* Don't add anything to the action bar if there are no entries */
229 		if (self->priv->command_bar && entries)
230 		{
231 			anjuta_command_bar_add_action_group (ANJUTA_COMMAND_BAR (self->priv->command_bar),
232 			                                     pane_name, entries, num_entries,
233 			                                     user_data);
234 
235 			g_signal_connect (G_OBJECT (dock_item), "selected",
236 			                  G_CALLBACK (on_pane_selected),
237 			                  self->priv->command_bar);
238 
239 			g_hash_table_insert (self->priv->dock_items, (gchar *) pane_name,
240 			                     dock_item);
241 
242 			/* Show the new pane's commands in the command bar */
243 			anjuta_command_bar_show_action_group (ANJUTA_COMMAND_BAR (self->priv->command_bar),
244 			                                      pane_name);
245 		}
246 
247 		return TRUE;
248 	}
249 
250 	return FALSE;
251 }
252 
253 /**
254  * anjuta_dock_replace_command_pane:
255  * @self: An AnjutaDock
256  * @pane_name: A unique name for this pane
257  * @pane_label: Label to display in this pane's grip
258  * @pane: The #AnjutaDockPane to add to the dock. The dock takes ownership of
259  *		  the pane object.
260  * @stock_icon: Stock icon to display in this pane's grip
261  * @placement: A #GdlDockPlacement value indicating where the pane should be
262  *			   placed
263  * @entries: (allow-none) (array length=num_entries): #AnjutaCommandBar entries
264  *			 for this pane. Can be %NULL
265  * @num_entries: The number of entries pointed to by entries, or 0.
266  * @user_data: User data to pass to the entry callback
267  *
268  * Adds a pane, with optional #AnjutaCommandBar entries, to an AnjutaDock. This
269  * method adds a pane with no grip that cannot be closed, floating or iconified.
270  * If there was an old command pane, that pane is removed in favour of the new pane.
271  */
272 void
anjuta_dock_replace_command_pane(AnjutaDock * self,const gchar * pane_name,const gchar * pane_label,const gchar * stock_icon,AnjutaDockPane * pane,GdlDockPlacement placement,AnjutaCommandBarEntry * entries,int num_entries,gpointer user_data)273 anjuta_dock_replace_command_pane (AnjutaDock *self,
274                                   const gchar *pane_name,
275                                   const gchar *pane_label, const gchar *stock_icon,
276                                   AnjutaDockPane *pane, GdlDockPlacement placement,
277                                   AnjutaCommandBarEntry *entries, int num_entries,
278                                   gpointer user_data)
279 {
280 	if (anjuta_dock_add_pane (self, pane_name, pane_label, stock_icon,
281 	                  		  pane, placement, entries, num_entries, user_data))
282 	{
283 		if (self->priv->command_pane)
284 		{
285 			anjuta_dock_remove_pane (self, self->priv->command_pane);
286 		}
287 
288 		self->priv->command_pane = pane;
289 	}
290 }
291 
292 /**
293  * anjuta_dock_remove_pane:
294  * @self An AnjutaDock
295  * @pane: Name of the pane to remove
296  *
297  * Removes a pane from a dock
298  */
299 void
anjuta_dock_remove_pane(AnjutaDock * self,AnjutaDockPane * pane)300 anjuta_dock_remove_pane (AnjutaDock *self, AnjutaDockPane *pane)
301 {
302 	GtkWidget *child;
303 	GtkContainer *dock_item;
304 
305 	child = anjuta_dock_pane_get_widget (pane);
306 
307 	if (self->priv->command_pane == pane)
308 	{
309 		self->priv->command_pane = NULL;
310 	}
311 
312 	if (child)
313 	{
314 		/* Remove the child from its dock item and destroy it */
315 		dock_item = g_object_get_data (G_OBJECT (child), "dock-item");
316 		g_hash_table_remove (self->priv->panes,
317 		                     g_object_get_data (G_OBJECT (dock_item),
318 		                                        "pane-name"));
319 		gtk_container_remove (dock_item, child);
320 
321 		gdl_dock_item_unbind (GDL_DOCK_ITEM (dock_item));
322 	}
323 }
324 
325 /**
326  * anjuta_dock_show_pane:
327  * @self: An AnjutaDock
328  * @pane: Name of the pane to show
329  *
330  * Makes the given pane visible
331  */
332 void
anjuta_dock_show_pane(AnjutaDock * self,AnjutaDockPane * pane)333 anjuta_dock_show_pane (AnjutaDock *self, AnjutaDockPane *pane)
334 {
335 	GtkWidget *child;
336 	GdlDockItem *dock_item;
337 
338 	child = anjuta_dock_pane_get_widget (pane);
339 
340 	if (child)
341 	{
342 		dock_item = g_object_get_data (G_OBJECT (child), "dock-item");
343 		gdl_dock_item_show_item (dock_item);
344 	}
345 }
346 
347 /**
348  * anjuta_dock_hide_pane:
349  * @self: An AnjutaDock
350  * @pane: Name of the pane to hide
351  *
352  * Makes the given pane invisible
353  */
354 void
anjuta_dock_hide_pane(AnjutaDock * self,AnjutaDockPane * pane)355 anjuta_dock_hide_pane (AnjutaDock *self, AnjutaDockPane *pane)
356 {
357 	GtkWidget *child;
358 	GdlDockItem *dock_item;
359 
360 	child = anjuta_dock_pane_get_widget (pane);
361 
362 	if (child)
363 	{
364 		dock_item = g_object_get_data (G_OBJECT (child), "dock-item");
365 		gdl_dock_item_hide_item (dock_item);
366 	}
367 }
368 
369 /**
370  * anjuta_dock_present_pane:
371  * @self: An AnjutaDock
372  * @pane: Pane to present
373  *
374  * Presents the pane to the user by making it the currently active pane in its
375  * switcher
376  */
377 void
anjuta_dock_present_pane(AnjutaDock * self,AnjutaDockPane * pane)378 anjuta_dock_present_pane (AnjutaDock *self, AnjutaDockPane *pane)
379 {
380 	GtkWidget *child;
381 	GdlDockObject *dock_item;
382 
383 	child = anjuta_dock_pane_get_widget (pane);
384 
385 	if (child)
386 	{
387 		dock_item = g_object_get_data (G_OBJECT (child), "dock-item");
388 		gdl_dock_object_present (dock_item, NULL);
389 	}
390 }
391 
392 /**
393  * anjuta_dock_set_command_bar:
394  * @self: An AnjutaDock
395  * @command_bar: An #AnjutaCommandBar to associate with this dock
396  *
397  * Associates an #AnjutaCommandBar with this dock. Command bars can be used to
398  * provide different sets of commands based on the currently visible pane.
399  */
400 void
anjuta_dock_set_command_bar(AnjutaDock * self,AnjutaCommandBar * command_bar)401 anjuta_dock_set_command_bar (AnjutaDock *self, AnjutaCommandBar *command_bar)
402 {
403 	if (self->priv->command_bar)
404 		g_object_unref (self->priv->command_bar);
405 
406 	self->priv->command_bar = g_object_ref (command_bar);
407 }
408 
409 /**
410  * anjuta_dock_get_command_bar:
411  * @self: An AnjutaDock
412  *
413  * Returns: (transfer none) (allow-none): the #AnjutaCommandBar associated with
414  * this dock or %NULL.
415  */
416 AnjutaCommandBar *
anjuta_dock_get_command_bar(AnjutaDock * self)417 anjuta_dock_get_command_bar (AnjutaDock *self)
418 {
419 	return ANJUTA_COMMAND_BAR (self->priv->command_bar);
420 }
421