1 /*
2 * Copyright (C) 2009 - 2011 Vivien Malerba <malerba@gnome-db.org>
3 * Copyright (C) 2010 David King <davidk@openismus.com>
4 * Copyright (C) 2011 Murray Cumming <murrayc@murrayc.com>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 */
20
21 #include <string.h>
22 #include <glib/gi18n-lib.h>
23 #include <libgda/binreloc/gda-binreloc.h>
24 #include "browser-connections-list.h"
25 #include "browser-core.h"
26 #include "browser-window.h"
27 #include "browser-connection.h"
28 #include <libgda-ui/gdaui-login.h>
29 #include "support.h"
30 #include "login-dialog.h"
31
32 /*
33 * Main static functions
34 */
35 static void browser_connections_list_class_init (BrowserConnectionsListClass *klass);
36 static void browser_connections_list_init (BrowserConnectionsList *stmt);
37 static void browser_connections_list_dispose (GObject *object);
38
39 static void connection_added_cb (BrowserCore *bcore, BrowserConnection *bcnc, BrowserConnectionsList *clist);
40 static void connection_removed_cb (BrowserCore *bcore, BrowserConnection *bcnc, BrowserConnectionsList *clist);
41
42 struct _BrowserConnectionsListPrivate
43 {
44 GtkGrid *layout_grid;
45 GtkTreeView *treeview;
46 gulong cnc_added_sigid;
47 gulong cnc_removed_sigid;
48
49 GtkWidget *cnc_params_editor;
50
51 GtkWidget *close_cnc_button;
52 };
53
54 /* get a pointer to the parents to be able to call their destructor */
55 static GObjectClass *parent_class = NULL;
56 static BrowserConnectionsList *_clist = NULL;
57
58 GType
browser_connections_list_get_type(void)59 browser_connections_list_get_type (void)
60 {
61 static GType type = 0;
62
63 if (G_UNLIKELY (type == 0)) {
64 static GMutex registering;
65 static const GTypeInfo info = {
66 sizeof (BrowserConnectionsListClass),
67 (GBaseInitFunc) NULL,
68 (GBaseFinalizeFunc) NULL,
69 (GClassInitFunc) browser_connections_list_class_init,
70 NULL,
71 NULL,
72 sizeof (BrowserConnectionsList),
73 0,
74 (GInstanceInitFunc) browser_connections_list_init,
75 0
76 };
77
78 g_mutex_lock (®istering);
79 if (type == 0)
80 type = g_type_register_static (GTK_TYPE_WINDOW, "BrowserConnectionsList", &info, 0);
81 g_mutex_unlock (®istering);
82 }
83 return type;
84 }
85
86 static void
browser_connections_list_class_init(BrowserConnectionsListClass * klass)87 browser_connections_list_class_init (BrowserConnectionsListClass * klass)
88 {
89 GObjectClass *object_class = G_OBJECT_CLASS (klass);
90 parent_class = g_type_class_peek_parent (klass);
91
92 object_class->dispose = browser_connections_list_dispose;
93 }
94
95 static void
browser_connections_list_init(BrowserConnectionsList * clist)96 browser_connections_list_init (BrowserConnectionsList *clist)
97 {
98 clist->priv = g_new0 (BrowserConnectionsListPrivate, 1);
99 clist->priv->treeview = NULL;
100 clist->priv->cnc_params_editor = NULL;
101 clist->priv->cnc_added_sigid = 0;
102 clist->priv->cnc_removed_sigid = 0;
103 }
104
105 static void
browser_connections_list_dispose(GObject * object)106 browser_connections_list_dispose (GObject *object)
107 {
108 BrowserConnectionsList *clist;
109
110 g_return_if_fail (object != NULL);
111 g_return_if_fail (BROWSER_IS_CONNECTIONS_LIST (object));
112
113 clist = BROWSER_CONNECTIONS_LIST (object);
114
115 if (clist->priv) {
116 if (clist->priv->cnc_added_sigid > 0)
117 g_signal_handler_disconnect (browser_core_get (), clist->priv->cnc_added_sigid);
118 if (clist->priv->cnc_removed_sigid > 0)
119 g_signal_handler_disconnect (browser_core_get (), clist->priv->cnc_removed_sigid);
120
121 g_free (clist->priv);
122 clist->priv = NULL;
123 }
124
125 /* parent class */
126 parent_class->dispose (object);
127 }
128
129 enum
130 {
131 COLUMN_BCNC,
132 NUM_COLUMNS
133 };
134
135 static gboolean
delete_event(GtkWidget * widget,G_GNUC_UNUSED GdkEvent * event,G_GNUC_UNUSED gpointer data)136 delete_event (GtkWidget *widget, G_GNUC_UNUSED GdkEvent *event, G_GNUC_UNUSED gpointer data)
137 {
138 gtk_widget_hide (widget);
139 return TRUE;
140 }
141
cell_name_data_func(G_GNUC_UNUSED GtkTreeViewColumn * tree_column,GtkCellRenderer * cell,GtkTreeModel * tree_model,GtkTreeIter * iter,G_GNUC_UNUSED gpointer data)142 static void cell_name_data_func (G_GNUC_UNUSED GtkTreeViewColumn *tree_column,
143 GtkCellRenderer *cell,
144 GtkTreeModel *tree_model,
145 GtkTreeIter *iter,
146 G_GNUC_UNUSED gpointer data)
147 {
148 BrowserConnection *bcnc;
149 gchar *str, *cncstr = NULL;
150 gchar *markup;
151 const GdaDsnInfo *cncinfo;
152
153 gtk_tree_model_get (tree_model, iter, COLUMN_BCNC, &bcnc, -1);
154 cncinfo = browser_connection_get_information (bcnc);
155 if (cncinfo) {
156 if (cncinfo->name)
157 cncstr = g_strdup_printf (_("DSN: %s"), cncinfo->name);
158 else if (cncinfo->provider)
159 cncstr = g_strdup_printf (_("Provider: %s"), cncinfo->provider);
160 }
161
162 markup = g_markup_escape_text (browser_connection_get_name (bcnc), -1);
163 if (cncstr)
164 str = g_strdup_printf ("%s\n<small>%s</small>",
165 markup, cncstr);
166 else
167 str = g_strdup (markup);
168 g_free (cncstr);
169 g_free (markup);
170
171 g_object_set ((GObject*) cell, "markup", str, NULL);
172 g_free (str);
173 g_object_unref (bcnc);
174 }
175
176 static void
selection_changed_cb(GtkTreeSelection * select,BrowserConnectionsList * clist)177 selection_changed_cb (GtkTreeSelection *select, BrowserConnectionsList *clist)
178 {
179 GtkTreeModel *model;
180 GtkTreeIter iter;
181 BrowserConnection *bcnc = NULL;
182 const GdaDsnInfo *cncinfo = NULL;
183
184 if (gtk_tree_selection_get_selected (select, &model, &iter)) {
185 gtk_tree_model_get (model, &iter, COLUMN_BCNC, &bcnc, -1);
186 cncinfo = browser_connection_get_information (bcnc);
187 g_object_unref (bcnc);
188
189 gtk_widget_set_sensitive (_clist->priv->close_cnc_button, TRUE);
190 }
191 else
192 gtk_widget_set_sensitive (_clist->priv->close_cnc_button, FALSE);
193
194 if (clist->priv->cnc_params_editor) {
195 gtk_widget_destroy (clist->priv->cnc_params_editor);
196 clist->priv->cnc_params_editor = NULL;
197 }
198
199 if (cncinfo && cncinfo->provider) {
200 /* create GdaSet for parameters to display */
201 GdaSet *dset;
202 GdaHolder *holder;
203 dset = gda_set_new_inline (1, "PROVIDER_NAME", G_TYPE_STRING, cncinfo->provider);
204 holder = GDA_HOLDER (dset->holders->data);
205 g_object_set (G_OBJECT (holder), "name", _("Database provider"), NULL);
206
207 GdaProviderInfo *pinfo;
208 pinfo = gda_config_get_provider_info (cncinfo->provider);
209 if (pinfo && pinfo->dsn_params)
210 gda_set_merge_with_set (dset, pinfo->dsn_params);
211
212 holder = gda_holder_new_inline (G_TYPE_STRING, "GDA_BROWSER_DICT_FILE", _("In memory"));
213 g_object_set (G_OBJECT (holder),
214 "name", _("Dictionary file"),
215 "description", _("File used to store any information associated\n"
216 "to this connection (favorites, descriptions, ...)"), NULL);
217 gda_set_add_holder (dset, holder);
218 g_object_unref (holder);
219 if (bcnc) {
220 const gchar *dict_file_name;
221 dict_file_name = browser_connection_get_dictionary_file (bcnc);
222
223 if (dict_file_name)
224 gda_set_set_holder_value (dset, NULL, "GDA_BROWSER_DICT_FILE",
225 dict_file_name);
226 }
227
228 /* create form */
229 GtkWidget *wid;
230 wid = gdaui_basic_form_new (dset);
231 g_object_set ((GObject*) wid, "show-actions", FALSE, NULL);
232 gdaui_basic_form_entry_set_editable (GDAUI_BASIC_FORM (wid), NULL, FALSE);
233 gtk_grid_attach (clist->priv->layout_grid, wid, 1, 2, 1, 1);
234 gtk_widget_show (wid);
235 clist->priv->cnc_params_editor = wid;
236
237 /* fill GdaSet's parameters with values */
238 if (cncinfo->cnc_string) {
239 gchar **array = NULL;
240
241 array = g_strsplit (cncinfo->cnc_string, ";", 0);
242 if (array) {
243 gint index = 0;
244 gchar *tok;
245 gchar *value;
246 gchar *name;
247
248 for (index = 0; array[index]; index++) {
249 name = strtok_r (array [index], "=", &tok);
250 if (name)
251 value = strtok_r (NULL, "=", &tok);
252 else
253 value = NULL;
254 if (name && value) {
255 GdaHolder *param;
256 gda_rfc1738_decode (name);
257 gda_rfc1738_decode (value);
258
259 param = gda_set_get_holder (dset, name);
260 if (param)
261 g_assert (gda_holder_set_value_str (param, NULL, value, NULL));
262 }
263 }
264
265 g_strfreev (array);
266 }
267 }
268
269 g_object_unref (dset);
270 }
271 }
272
273 static void
connection_close_cb(G_GNUC_UNUSED GtkButton * button,BrowserConnectionsList * clist)274 connection_close_cb (G_GNUC_UNUSED GtkButton *button, BrowserConnectionsList *clist)
275 {
276 GtkTreeSelection *select;
277 GtkTreeModel *model;
278 GtkTreeIter iter;
279
280 select = gtk_tree_view_get_selection (clist->priv->treeview);
281 if (gtk_tree_selection_get_selected (select, &model, &iter)) {
282 BrowserConnection *bcnc;
283 gtk_tree_model_get (model, &iter, COLUMN_BCNC, &bcnc, -1);
284 g_object_unref (bcnc);
285 browser_connection_close (NULL, bcnc);
286 }
287 }
288
289 static void
connection_new_cb(G_GNUC_UNUSED GtkButton * button,G_GNUC_UNUSED BrowserConnectionsList * clist)290 connection_new_cb (G_GNUC_UNUSED GtkButton *button, G_GNUC_UNUSED BrowserConnectionsList *clist)
291 {
292 LoginDialog *dialog;
293 GdaConnection *cnc;
294 GError *error = NULL;
295 dialog = login_dialog_new (NULL);
296
297 cnc = login_dialog_run (dialog, TRUE, &error);
298 if (cnc) {
299 BrowserConnection *bcnc;
300 BrowserWindow *nbwin;
301 bcnc = browser_connection_new (cnc);
302 nbwin = browser_window_new (bcnc, NULL);
303
304 browser_core_take_window (nbwin);
305 browser_core_take_connection (bcnc);
306 }
307 }
308
309 /**
310 * browser_connections_list_show
311 * @current: (allow-none): a connection to select for displaed properties, or %NULL
312 *
313 * Creates a new #BrowserConnectionsList widget and displays it.
314 * Only one is created and shown (singleton)
315 *
316 * Returns: the new object
317 */
318 void
browser_connections_list_show(BrowserConnection * current)319 browser_connections_list_show (BrowserConnection *current)
320 {
321 if (!_clist) {
322 GtkWidget *clist, *sw, *grid, *treeview, *label, *wid;
323 gchar *str;
324 clist = GTK_WIDGET (g_object_new (BROWSER_TYPE_CONNECTIONS_LIST,
325 NULL));
326 gtk_window_set_default_size ((GtkWindow*) clist, 550, 450);
327 _clist = (BrowserConnectionsList *) clist;
328 gtk_window_set_title (GTK_WINDOW (clist), _("Opened connections"));
329 gtk_container_set_border_width (GTK_CONTAINER (clist), 6);
330 g_signal_connect (G_OBJECT (clist), "delete-event",
331 G_CALLBACK (delete_event), NULL);
332
333 str = gda_gbr_get_file_path (GDA_DATA_DIR, LIBGDA_ABI_NAME, "pixmaps", "gda-browser-connected.png", NULL);
334 gtk_window_set_icon_from_file (GTK_WINDOW (clist), str, NULL);
335 g_free (str);
336
337 /* table layout */
338 grid = gtk_grid_new ();
339 gtk_grid_set_column_spacing (GTK_GRID (grid), 10);
340 gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
341 gtk_container_add (GTK_CONTAINER (clist), grid);
342 _clist->priv->layout_grid = GTK_GRID (grid);
343
344 /* image and explaining label */
345 GtkWidget *hbox;
346 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
347 gtk_grid_attach (GTK_GRID (grid), hbox, 0, 0, 3, 1);
348
349 str = gda_gbr_get_file_path (GDA_DATA_DIR, LIBGDA_ABI_NAME, "pixmaps", "gda-browser-connected-big.png", NULL);
350 wid = gtk_image_new_from_file (str);
351 g_free (str);
352 gtk_box_pack_start (GTK_BOX (hbox), wid, FALSE, FALSE, 0);
353
354 wid = gtk_label_new ("");
355 str = g_strdup_printf ("<big><b>%s:\n</b></big>%s",
356 _("List of opened connections"),
357 "The connection properties are read-only.");
358 gtk_label_set_markup (GTK_LABEL (wid), str);
359 g_free (str);
360 gtk_misc_set_alignment (GTK_MISC (wid), 0., -1);
361 gtk_box_pack_start (GTK_BOX (hbox), wid, TRUE, FALSE, 6);
362
363 /* left column */
364 label = gtk_label_new ("");
365 str = g_strdup_printf ("<b>%s:</b>", _("Connections"));
366 gtk_label_set_markup (GTK_LABEL (label), str);
367 g_free (str);
368 gtk_misc_set_alignment (GTK_MISC (label), 0., -1);
369 gtk_grid_attach (GTK_GRID (grid), label, 0, 1, 1, 1);
370
371 sw = gtk_scrolled_window_new (NULL, NULL);
372 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw),
373 GTK_SHADOW_ETCHED_IN);
374 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
375 GTK_POLICY_NEVER,
376 GTK_POLICY_AUTOMATIC);
377 gtk_grid_attach (GTK_GRID (grid), sw, 0, 2, 1, 2);
378
379 /* connection's properties */
380 label = gtk_label_new ("");
381 str = g_strdup_printf ("<b>%s:</b>", _("Connection's properties"));
382 gtk_label_set_markup (GTK_LABEL (label), str);
383 g_free (str);
384 gtk_misc_set_alignment (GTK_MISC (label), 0., -1);
385 gtk_grid_attach (GTK_GRID (grid), label, 1, 1, 1, 1);
386
387 /* buttons at the bottom*/
388 GtkWidget *bbox, *button;
389 bbox = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL);
390 gtk_grid_attach (GTK_GRID (grid), bbox, 1, 3, 1, 1);
391 gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), GTK_BUTTONBOX_END);
392 button = gtk_button_new_with_label (_("Close connection"));
393 gtk_box_pack_start (GTK_BOX (bbox), button, TRUE, TRUE, 0);
394 g_signal_connect (button, "clicked",
395 G_CALLBACK (connection_close_cb), clist);
396 gtk_widget_set_tooltip_text (button, _("Close selected connection"));
397 _clist->priv->close_cnc_button = button;
398
399 button = gtk_button_new_with_label (_("Connect"));
400 gtk_box_pack_start (GTK_BOX (bbox), button, TRUE, TRUE, 0);
401 g_signal_connect (button, "clicked",
402 G_CALLBACK (connection_new_cb), clist);
403 gtk_widget_set_tooltip_text (button, _("Open a new connection"));
404
405 /* GtkTreeModel and view */
406 GtkListStore *store;
407 store = gtk_list_store_new (NUM_COLUMNS,
408 BROWSER_TYPE_CONNECTION);
409
410 treeview = browser_make_tree_view (GTK_TREE_MODEL (store));
411 _clist->priv->treeview = GTK_TREE_VIEW (treeview);
412 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
413 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (treeview), TRUE);
414 g_object_unref (G_OBJECT (store));
415 gtk_container_add (GTK_CONTAINER (sw), treeview);
416
417 /* treeview's columns */
418 GtkTreeViewColumn *col;
419 GtkCellRenderer *cell;
420 cell = gtk_cell_renderer_text_new ();
421 col = gtk_tree_view_column_new ();
422 gtk_tree_view_column_pack_start (col, cell, TRUE);
423 gtk_tree_view_column_set_cell_data_func (col, cell,
424 (GtkTreeCellDataFunc) cell_name_data_func, NULL, NULL);
425 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), col);
426
427 /* selection handling */
428 GtkTreeSelection *select;
429 select = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
430 gtk_tree_selection_set_mode (select, GTK_SELECTION_SINGLE);
431 g_signal_connect (G_OBJECT (select), "changed",
432 G_CALLBACK (selection_changed_cb), clist);
433
434
435 /* initial filling */
436 GSList *connections, *list;
437 connections = browser_core_get_connections ();
438 for (list = connections; list; list = list->next)
439 connection_added_cb (browser_core_get(), BROWSER_CONNECTION (list->data),
440 (BrowserConnectionsList*) clist);
441 g_slist_free (connections);
442
443 _clist->priv->cnc_added_sigid = g_signal_connect (browser_core_get (), "connection-added",
444 G_CALLBACK (connection_added_cb), _clist);
445 _clist->priv->cnc_removed_sigid = g_signal_connect (browser_core_get (), "connection-removed",
446 G_CALLBACK (connection_removed_cb), _clist);
447
448 gtk_widget_show_all (clist);
449 }
450 else {
451 gtk_window_set_screen (GTK_WINDOW (_clist), gdk_screen_get_default ()); /* FIXME: specify GdkScreen */
452 gtk_window_present (GTK_WINDOW (_clist));
453 }
454
455 if (current) {
456 GtkTreeModel *model;
457 GtkTreeIter iter;
458 model = gtk_tree_view_get_model (GTK_TREE_VIEW (_clist->priv->treeview));
459 if (gtk_tree_model_get_iter_first (model, &iter)) {
460 do {
461 BrowserConnection *bcnc;
462 gtk_tree_model_get (model, &iter, COLUMN_BCNC, &bcnc, -1);
463 g_object_unref (bcnc);
464 if (bcnc == current) {
465 GtkTreeSelection *select;
466 select = gtk_tree_view_get_selection (GTK_TREE_VIEW (_clist->priv->treeview));
467 gtk_tree_selection_select_iter (select, &iter);
468 break;
469 }
470 } while (gtk_tree_model_iter_next (model, &iter));
471 }
472 }
473 else {
474 /* select the 1st available */
475 GtkTreeModel *model;
476 GtkTreeIter iter;
477 model = gtk_tree_view_get_model (GTK_TREE_VIEW (_clist->priv->treeview));
478 if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (model), &iter)) {
479 GtkTreeSelection *select;
480 select = gtk_tree_view_get_selection (GTK_TREE_VIEW (_clist->priv->treeview));
481 gtk_tree_selection_select_iter (select, &iter);
482 }
483 }
484 }
485
486 static void
connection_added_cb(G_GNUC_UNUSED BrowserCore * bcore,BrowserConnection * bcnc,BrowserConnectionsList * clist)487 connection_added_cb (G_GNUC_UNUSED BrowserCore *bcore, BrowserConnection *bcnc, BrowserConnectionsList *clist)
488 {
489 GtkListStore *store;
490 GtkTreeIter iter;
491
492 store = GTK_LIST_STORE (gtk_tree_view_get_model (clist->priv->treeview));
493 gtk_list_store_append (store, &iter);
494 gtk_list_store_set (store, &iter,
495 COLUMN_BCNC, bcnc, -1);
496 }
497
498 static void
connection_removed_cb(G_GNUC_UNUSED BrowserCore * bcore,BrowserConnection * bcnc,BrowserConnectionsList * clist)499 connection_removed_cb (G_GNUC_UNUSED BrowserCore *bcore, BrowserConnection *bcnc, BrowserConnectionsList *clist)
500 {
501 GtkTreeModel *model;
502 GtkTreeIter iter;
503 GtkTreeSelection *select;
504 gboolean is_selected_item = FALSE;
505
506 select = gtk_tree_view_get_selection (clist->priv->treeview);
507 model = gtk_tree_view_get_model (clist->priv->treeview);
508 g_assert (gtk_tree_model_get_iter_first (model, &iter));
509
510 do {
511 BrowserConnection *mbcnc;
512 gtk_tree_model_get (model, &iter, COLUMN_BCNC, &mbcnc, -1);
513 g_object_unref (mbcnc);
514 if (mbcnc == bcnc) {
515 is_selected_item = gtk_tree_selection_iter_is_selected (select, &iter);
516 gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
517 break;
518 }
519 } while (gtk_tree_model_iter_next (model, &iter));
520
521 /* select the 1st available */
522 if (is_selected_item && gtk_tree_model_get_iter_first (model, &iter))
523 gtk_tree_selection_select_iter (select, &iter);
524 }
525