1 /*
2  * Copyright (C) 2011 Murray Cumming <murrayc@murrayc.com>
3  * Copyright (C) 2011 Vivien Malerba <malerba@gnome-db.org>
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18  */
19 
20 #include <glib/gi18n-lib.h>
21 #include <string.h>
22 #include "classes-view.h"
23 #include "../dnd.h"
24 #include "../support.h"
25 #include "../gdaui-bar.h"
26 #include "../browser-stock-icons.h"
27 #include <virtual/gda-ldap-connection.h>
28 #include "mgr-ldap-classes.h"
29 #include <libgda-ui/gdaui-tree-store.h>
30 #include <libgda/gda-debug-macros.h>
31 
32 struct _ClassesViewPrivate {
33 	BrowserConnection *bcnc;
34 
35 	GdaTree           *classes_tree;
36 	GdauiTreeStore    *classes_store;
37 
38 	gchar             *current_class;
39 };
40 
41 static void classes_view_class_init (ClassesViewClass *klass);
42 static void classes_view_init       (ClassesView *eview, ClassesViewClass *klass);
43 static void classes_view_dispose   (GObject *object);
44 
45 static GObjectClass *parent_class = NULL;
46 
47 
48 /*
49  * ClassesView class implementation
50  */
51 
52 static void
classes_view_class_init(ClassesViewClass * klass)53 classes_view_class_init (ClassesViewClass *klass)
54 {
55 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
56 
57 	parent_class = g_type_class_peek_parent (klass);
58 
59 	object_class->dispose = classes_view_dispose;
60 }
61 
62 static void
classes_view_init(ClassesView * eview,G_GNUC_UNUSED ClassesViewClass * klass)63 classes_view_init (ClassesView *eview, G_GNUC_UNUSED ClassesViewClass *klass)
64 {
65 	eview->priv = g_new0 (ClassesViewPrivate, 1);
66 	eview->priv->current_class = NULL;
67 }
68 
69 static void
classes_view_dispose(GObject * object)70 classes_view_dispose (GObject *object)
71 {
72 	ClassesView *eview = (ClassesView *) object;
73 
74 	/* free memory */
75 	if (eview->priv) {
76 		if (eview->priv->bcnc)
77 			g_object_unref (eview->priv->bcnc);
78 		if (eview->priv->classes_tree)
79 			g_object_unref (eview->priv->classes_tree);
80 
81 		g_free (eview->priv);
82 		eview->priv = NULL;
83 	}
84 
85 	parent_class->dispose (object);
86 }
87 
88 GType
classes_view_get_type(void)89 classes_view_get_type (void)
90 {
91 	static GType type = 0;
92 
93 	if (G_UNLIKELY (type == 0)) {
94 		static const GTypeInfo info = {
95 			sizeof (ClassesViewClass),
96 			(GBaseInitFunc) NULL,
97 			(GBaseFinalizeFunc) NULL,
98 			(GClassInitFunc) classes_view_class_init,
99 			NULL,
100 			NULL,
101 			sizeof (ClassesView),
102 			0,
103 			(GInstanceInitFunc) classes_view_init,
104 			0
105 		};
106 
107 		type = g_type_register_static (GTK_TYPE_TREE_VIEW, "ClassesView", &info, 0);
108 	}
109 	return type;
110 }
111 
112 static gchar *
classes_view_to_selection(G_GNUC_UNUSED ClassesView * eview)113 classes_view_to_selection (G_GNUC_UNUSED ClassesView *eview)
114 {
115 	/*
116 	  GString *string;
117 	  gchar *tmp;
118 	  string = g_string_new ("OBJ_TYPE=classes");
119 	  tmp = gda_rfc1738_encode (eview->priv->schema);
120 	  g_string_append_printf (string, ";OBJ_SCHEMA=%s", tmp);
121 	  g_free (tmp);
122 	  tmp = gda_rfc1738_encode (eview->priv->classes_name);
123 	  g_string_append_printf (string, ";OBJ_NAME=%s", tmp);
124 	  g_free (tmp);
125 	  tmp = gda_rfc1738_encode (eview->priv->classes_short_name);
126 	  g_string_append_printf (string, ";OBJ_SHORT_NAME=%s", tmp);
127 	  g_free (tmp);
128 	  return g_string_free (string, FALSE);
129 	*/
130 	if (eview->priv->current_class)
131 		return g_strdup (eview->priv->current_class);
132 	else
133 		return NULL;
134 }
135 
136 static void
source_drag_data_get_cb(G_GNUC_UNUSED GtkWidget * widget,G_GNUC_UNUSED GdkDragContext * context,GtkSelectionData * selection_data,guint view,G_GNUC_UNUSED guint time,ClassesView * eview)137 source_drag_data_get_cb (G_GNUC_UNUSED GtkWidget *widget, G_GNUC_UNUSED GdkDragContext *context,
138 			 GtkSelectionData *selection_data,
139 			 guint view, G_GNUC_UNUSED guint time, ClassesView *eview)
140 {
141 	switch (view) {
142 	case TARGET_KEY_VALUE: {
143 		gchar *str;
144 		str = classes_view_to_selection (eview);
145 		gtk_selection_data_set (selection_data,
146 					gtk_selection_data_get_target (selection_data), 8, (guchar*) str,
147 					strlen (str));
148 		g_free (str);
149 		break;
150 	}
151 	default:
152 	case TARGET_PLAIN: {
153 		gtk_selection_data_set_text (selection_data, classes_view_get_current_class (eview), -1);
154 		break;
155 	}
156 	case TARGET_ROOTWIN:
157 		TO_IMPLEMENT; /* dropping on the Root Window => create a file */
158 		break;
159 	}
160 }
161 
162 static void selection_changed_cb (GtkTreeSelection *sel, ClassesView *eview);
163 
164 enum {
165 	COLUMN_CLASS,
166 	COLUMN_ICON,
167 	COLUMN_NAME,
168 	NUM_COLUMNS
169 };
170 
171 static void
text_cell_data_func(G_GNUC_UNUSED GtkTreeViewColumn * tree_column,GtkCellRenderer * cell,GtkTreeModel * tree_model,GtkTreeIter * iter,G_GNUC_UNUSED gpointer data)172 text_cell_data_func (G_GNUC_UNUSED GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
173 		     GtkTreeModel *tree_model, GtkTreeIter *iter, G_GNUC_UNUSED gpointer data)
174 {
175 	gchar *tmp;
176 	gtk_tree_model_get (tree_model, iter,
177                             COLUMN_CLASS, &tmp, -1);
178 	if (tmp) {
179 		g_object_set ((GObject*) cell, "text", tmp,
180 			      "weight-set", FALSE,
181 			      "background-set", FALSE,
182 			      NULL);
183 		g_free (tmp);
184 	}
185 	else {
186 		gtk_tree_model_get (tree_model, iter,
187 				    COLUMN_NAME, &tmp, -1);
188 		g_object_set ((GObject*) cell, "text", tmp,
189 			      "weight", PANGO_WEIGHT_BOLD,
190 			      "background", "grey", NULL);
191 		g_free (tmp);
192 	}
193 }
194 
195 /**
196  * classes_view_new
197  *
198  * Returns: a new #GtkWidget
199  */
200 GtkWidget *
classes_view_new(BrowserConnection * bcnc,const gchar * classname)201 classes_view_new (BrowserConnection *bcnc, const gchar *classname)
202 {
203 	ClassesView *eview;
204 
205 	g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), NULL);
206 
207 	eview = CLASSES_VIEW (g_object_new (CLASSES_VIEW_TYPE, NULL));
208 	eview->priv->bcnc = g_object_ref ((GObject*) bcnc);
209 	g_signal_connect (eview, "drag-data-get",
210 			  G_CALLBACK (source_drag_data_get_cb), eview);
211 
212 	GdaTreeManager *mgr;
213 	GtkTreeModel *store;
214 	GtkCellRenderer *renderer;
215         GtkTreeViewColumn *column;
216 	eview->priv->classes_tree = gda_tree_new ();
217 	mgr = mgr_ldap_classes_new (eview->priv->bcnc, FALSE, NULL);
218 	gda_tree_add_manager (eview->priv->classes_tree, mgr);
219 	gda_tree_manager_add_manager (mgr, mgr);
220 	gda_tree_update_all (eview->priv->classes_tree, NULL);
221 	g_object_unref (mgr);
222 
223 	store = gdaui_tree_store_new (eview->priv->classes_tree, NUM_COLUMNS,
224 				      G_TYPE_STRING, "class",
225 				      G_TYPE_OBJECT, "icon",
226 				      G_TYPE_STRING, GDA_ATTRIBUTE_NAME);
227 	gtk_tree_view_set_model (GTK_TREE_VIEW (eview), GTK_TREE_MODEL (store));
228 
229 	eview->priv->classes_store = GDAUI_TREE_STORE (store);
230 	g_object_unref (G_OBJECT (store));
231 
232 	column = gtk_tree_view_column_new ();
233 
234 	renderer = gtk_cell_renderer_pixbuf_new ();
235         gtk_tree_view_column_pack_start (column, renderer, FALSE);
236         gtk_tree_view_column_add_attribute (column, renderer, "pixbuf", COLUMN_ICON);
237         g_object_set ((GObject*) renderer, "yalign", 0., NULL);
238 
239 	renderer = gtk_cell_renderer_text_new ();
240 	gtk_tree_view_column_pack_start (column, renderer, TRUE);
241 	gtk_tree_view_column_set_cell_data_func (column, renderer,
242 						 (GtkTreeCellDataFunc) text_cell_data_func,
243 						 NULL, NULL);
244 
245         gtk_tree_view_append_column (GTK_TREE_VIEW (eview), column);
246 	gtk_tree_view_set_expander_column (GTK_TREE_VIEW (eview), column);
247 	gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (eview), FALSE);
248 
249 	/* tree selection */
250 	GtkTreeSelection *sel;
251 	sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (eview));
252 	gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
253 	g_signal_connect (sel, "changed",
254 			  G_CALLBACK (selection_changed_cb), eview);
255 
256 	if (classname)
257 		classes_view_set_current_class (eview, classname);
258 
259 	return (GtkWidget*) eview;
260 }
261 
262 static void
selection_changed_cb(GtkTreeSelection * sel,ClassesView * eview)263 selection_changed_cb (GtkTreeSelection *sel, ClassesView *eview)
264 {
265 	GtkTreeIter iter;
266 	GtkTreeModel *model;
267 	if (gtk_tree_selection_get_selected (sel, &model, &iter)) {
268 		GdaTreeNode *node;
269 		const GValue *cvalue;
270 		node = gdaui_tree_store_get_node (GDAUI_TREE_STORE (model), &iter);
271 		g_assert (node);
272 		cvalue = gda_tree_node_get_node_attribute (node, "class");
273 		g_free (eview->priv->current_class);
274 		if (cvalue)
275 			eview->priv->current_class = g_value_dup_string (cvalue);
276 		else
277 			eview->priv->current_class = NULL;
278 	}
279 }
280 
281 /**
282  * classes_view_get_current_class:
283  */
284 const gchar *
classes_view_get_current_class(ClassesView * eview)285 classes_view_get_current_class (ClassesView *eview)
286 {
287 	g_return_val_if_fail (IS_CLASSES_VIEW (eview), NULL);
288 	return eview->priv->current_class;
289 }
290 
291 static GtkTreePath *
search_for_class(GtkTreeModel * model,const gchar * classname,GtkTreeIter * iter)292 search_for_class (GtkTreeModel *model, const gchar *classname, GtkTreeIter *iter)
293 {
294 #ifdef GDA_DEBUG_NO
295 	GtkTreePath *debug;
296 	if (iter) {
297 		debug = gtk_tree_model_get_path (model, iter);
298 		g_print ("%s (%s)\n", __FUNCTION__, gtk_tree_path_to_string (debug));
299 		gtk_tree_path_free (debug);
300 	}
301 	else
302 		g_print ("%s (TOP)\n", __FUNCTION__);
303 #endif
304 
305 	if (iter) {
306 		/* look in the node itself */
307 		gchar *cln = NULL;
308 		gtk_tree_model_get (model, iter, COLUMN_CLASS, &cln, -1);
309 		if (cln && !strcmp (cln, classname)) {
310 			g_free (cln);
311 			return gtk_tree_model_get_path (model, iter);
312 		}
313 		g_free (cln);
314 	}
315 
316 	/* look at the children */
317 	GtkTreeIter chiter;
318 	if (gtk_tree_model_iter_children (model, &chiter, iter)) {
319 		GtkTreePath *path;
320 		path = search_for_class (model, classname, &chiter);
321 		if (path)
322 			return path;
323 
324 		for (; gtk_tree_model_iter_next (model, &chiter); ) {
325 			GtkTreePath *path;
326 			path = search_for_class (model, classname, &chiter);
327 			if (path)
328 				return path;
329 		}
330 	}
331 
332 	return NULL;
333 }
334 
335 /**
336  * classes_view_set_current_class:
337  */
338 void
classes_view_set_current_class(ClassesView * eview,const gchar * classname)339 classes_view_set_current_class (ClassesView *eview, const gchar *classname)
340 {
341 	GtkTreePath *path = NULL;
342 	g_return_if_fail (IS_CLASSES_VIEW (eview));
343 	g_return_if_fail (classname && *classname);
344 
345 	path = search_for_class (GTK_TREE_MODEL (eview->priv->classes_store), classname, NULL);
346 	if (path) {
347 		GtkTreeSelection *sel;
348 
349 #ifdef GDA_DEBUG_NO
350 		g_print ("Found class [%s] at %s\n", classname, gtk_tree_path_to_string (path));
351 #endif
352 		gtk_tree_view_expand_to_path (GTK_TREE_VIEW (eview), path);
353 		gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (eview), path, NULL, TRUE, .5, 0.);
354 
355 		sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (eview));
356 		gtk_tree_selection_select_path (sel, path);
357 		gtk_tree_path_free (path);
358 	}
359 }
360