[libgda] Initial LDAP support (read only)
- From: Vivien Malerba <vivien src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libgda] Initial LDAP support (read only)
- Date: Wed, 1 Jun 2011 14:35:50 +0000 (UTC)
commit 45b1ad1345d9f2f1475ad263c1305c15b19e01c7
Author: Vivien Malerba <malerba gnome-db org>
Date: Wed Jun 1 16:19:29 2011 +0200
Initial LDAP support (read only)
Makefile.am | 5 +-
configure.ac | 11 +-
doc/C/libgda-5.0-docs.sgml | 15 +
doc/C/libgda-sections.txt | 66 +
doc/C/libgda-ui-sections.txt | 2 +
doc/C/libgda.types.in | 3 +
doc/C/limitations.xml | 6 +
doc/C/prov-notes.xml | 24 +
installers/Windows/etc/{gtk-2.0 => gtk-3.0}/gtkrc | 0
installers/Windows/gda-browser.nsi | 4 +
installers/Windows/make-zip-setup.sh | 46 +-
libgda-report/engine/Makefile.am | 1 +
libgda-ui/gdaui-tree-store.c | 357 ++++--
libgda-ui/gdaui-tree-store.h | 18 +-
libgda-ui/libgda-ui.symbols | 2 +
libgda/Makefile.am | 12 +-
libgda/gda-attributes-manager.h | 6 +
libgda/gda-data-model-ldap.c | 408 ++++++
libgda/gda-data-model-ldap.h | 92 ++
libgda/gda-data-model.c | 2 +-
libgda/gda-data-model.h | 3 +-
libgda/gda-data-select.c | 2 +-
libgda/gda-tree-mgr-ldap.c | 313 +++++
libgda/gda-tree-mgr-ldap.h | 70 +
libgda/gda-tree-node.c | 24 +-
libgda/gda-tree.c | 94 +-
libgda/gda-tree.h | 1 +
libgda/libgda.h.in | 2 +
libgda/libgda.symbols | 27 +-
libgda/sqlite/gda-sqlite-provider.c | 27 +-
libgda/sqlite/gda-sqlite-recordset.c | 10 +-
libgda/sqlite/virtual/.gitignore | 1 +
libgda/sqlite/virtual/Makefile.am | 11 +-
libgda/sqlite/virtual/gda-ldap-connection.c | 1025 ++++++++++++++
libgda/sqlite/virtual/gda-ldap-connection.h | 232 ++++
libgda/sqlite/virtual/gda-vprovider-data-model.c | 68 +-
.../{libgda-virtual.h => libgda-virtual.h.in} | 6 +-
m4/ldap.m4 | 181 +++
po/POTFILES.in | 23 +-
providers/Makefile.am | 7 +-
providers/ldap/Makefile.am | 40 +
providers/ldap/gda-ldap-provider.c | 927 +++++++++++++
providers/ldap/gda-ldap-provider.h | 50 +
providers/ldap/gda-ldap-util.c | 1410 +++++++++++++++++++
providers/ldap/gda-ldap-util.h | 57 +
providers/ldap/gda-ldap.h | 54 +
providers/ldap/gdaprov-data-model-ldap.c | 1447 ++++++++++++++++++++
.../ldap/gdaprov-data-model-ldap.h | 24 +-
providers/ldap/ldap_specs_auth.xml.in | 7 +
providers/ldap/ldap_specs_dsn.xml.in | 12 +
providers/ldap/libgda-ldap-5.0.pc.in | 9 +
providers/ldap/libmain.c | 104 ++
samples/LdapBrowser/README | 22 +
samples/LdapBrowser/ldap-browser.c | 276 ++++
samples/Makefile | 2 +-
testing/Makefile.am | 1 +
tests/data-models/check_pmodel.c | 2 +-
tools/.gitignore | 1 +
tools/Makefile.am | 1 +
tools/browser/Makefile.am | 22 +-
tools/browser/auth-dialog.c | 7 +
tools/browser/browser-connection.c | 509 +++++++-
tools/browser/browser-connection.h | 65 +
tools/browser/browser-favorites.c | 28 +-
tools/browser/browser-favorites.h | 14 +-
tools/browser/browser-perspective.c | 138 ++
tools/browser/browser-perspective.h | 4 +
tools/browser/browser-stock-icons.c | 4 +-
tools/browser/browser-stock-icons.h | 5 +-
tools/browser/browser-window.c | 9 +-
tools/browser/common/Makefile.am | 8 +-
tools/browser/common/ui-formgrid.c | 113 ++-
.../data-manager/data-manager-perspective.c | 135 +--
tools/browser/data/Makefile.am | 3 +
.../data/hicolor_actions_24x24_table-add.png | Bin 0 -> 914 bytes
.../data/hicolor_actions_32x32_ldap-entries.png | Bin 0 -> 1322 bytes
.../data/hicolor_actions_32x32_table-add.png | Bin 0 -> 1916 bytes
tools/browser/decl.h | 6 +-
tools/browser/doc/Makefile.am | 12 +-
tools/browser/doc/gda-browser-sections.txt | 4 +
.../browser/dummy-perspective/dummy-perspective.c | 17 +-
tools/browser/gda-browser-ldap-class-a.png | Bin 0 -> 513 bytes
tools/browser/gda-browser-ldap-class-s.png | Bin 0 -> 394 bytes
tools/browser/gda-browser-ldap-class-u.png | Bin 0 -> 514 bytes
tools/browser/gda-browser-ldap-class-x.png | Bin 0 -> 506 bytes
tools/browser/gda-browser-ldap-entry.png | Bin 0 -> 560 bytes
tools/browser/gda-browser-ldap-group.png | Bin 0 -> 808 bytes
tools/browser/gda-browser-ldap-organization.png | Bin 0 -> 590 bytes
tools/browser/gda-browser-ldap-person.png | Bin 0 -> 796 bytes
tools/browser/help/C/features.page | 2 +
.../browser/help/C/figures/ldap-browser-persp.png | Bin 0 -> 107744 bytes
tools/browser/help/C/figures/ldap-classes.png | Bin 0 -> 162544 bytes
tools/browser/help/C/figures/ldap-search.png | Bin 0 -> 104938 bytes
.../browser/help/C/figures/ldap-table-mapping.png | Bin 0 -> 83895 bytes
tools/browser/help/C/index.page | 3 +-
tools/browser/help/C/ldap-browser-perspective.page | 61 +
tools/browser/help/C/ldap-connections.page | 132 ++
tools/browser/help/C/sql-sqlite.page | 100 ++
tools/browser/help/C/virtual-connections.page | 12 +
tools/browser/help/Makefile.am | 10 +-
tools/browser/ldap-browser/Makefile.am | 58 +
tools/browser/ldap-browser/class-properties.c | 576 ++++++++
tools/browser/ldap-browser/class-properties.h | 59 +
tools/browser/ldap-browser/classes-view.c | 408 ++++++
tools/browser/ldap-browser/classes-view.h | 58 +
tools/browser/ldap-browser/entry-properties.c | 926 +++++++++++++
tools/browser/ldap-browser/entry-properties.h | 60 +
tools/browser/ldap-browser/filter-editor.c | 262 ++++
tools/browser/ldap-browser/filter-editor.h | 64 +
tools/browser/ldap-browser/hierarchy-view.c | 626 +++++++++
tools/browser/ldap-browser/hierarchy-view.h | 58 +
.../ldap-browser/ldap-browser-perspective.c | 531 +++++++
.../ldap-browser/ldap-browser-perspective.h | 69 +
tools/browser/ldap-browser/ldap-classes-page.c | 627 +++++++++
tools/browser/ldap-browser/ldap-classes-page.h | 58 +
tools/browser/ldap-browser/ldap-entries-page.c | 632 +++++++++
tools/browser/ldap-browser/ldap-entries-page.h | 58 +
.../browser/ldap-browser/ldap-favorite-selector.c | 657 +++++++++
.../browser/ldap-browser/ldap-favorite-selector.h | 59 +
tools/browser/ldap-browser/ldap-search-page.c | 538 ++++++++
tools/browser/ldap-browser/ldap-search-page.h | 56 +
tools/browser/ldap-browser/marshal.list | 27 +
tools/browser/ldap-browser/mgr-ldap-classes.c | 347 +++++
tools/browser/ldap-browser/mgr-ldap-classes.h | 56 +
tools/browser/ldap-browser/mgr-ldap-entries.c | 309 +++++
tools/browser/ldap-browser/mgr-ldap-entries.h | 56 +
tools/browser/ldap-browser/perspective-main.c | 37 +
tools/browser/ldap-browser/perspective-main.h | 29 +
tools/browser/ldap-browser/vtable-dialog.c | 186 +++
tools/browser/ldap-browser/vtable-dialog.h | 57 +
tools/browser/main.c | 8 +-
tools/browser/mgr-favorites.c | 231 +++-
tools/browser/query-exec/Makefile.am | 4 +-
.../{query-console.c => query-console-page.c} | 170 ++--
tools/browser/query-exec/query-console-page.h | 57 +
tools/browser/query-exec/query-console.h | 57 -
tools/browser/query-exec/query-exec-perspective.c | 167 +--
tools/browser/schema-browser/Makefile.am | 6 +
.../schema-browser/schema-browser-perspective.c | 76 +-
tools/browser/schema-browser/table-columns.c | 187 +++-
tools/browser/support.c | 49 +-
tools/browser/support.h | 18 +-
142 files changed, 16982 insertions(+), 668 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index 6d0fc6c..37093fd 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -99,7 +99,10 @@ example_files = \
samples/Blobs/testblob.db \
samples/Gir/Makefile \
samples/Gir/README \
- samples/Gir/libgda.js
+ samples/Gir/libgda.js \
+ samples/LdapBrowser/Makefile \
+ samples/LdapBrowser/README \
+ samples/LdapBrowser/ldap-browser.c
EXTRA_DIST = \
COPYING \
diff --git a/configure.ac b/configure.ac
index dd77c68..e56064a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -12,6 +12,7 @@ m4_include(m4/mysql.m4)
m4_include(m4/postgresql.m4)
m4_include(m4/oracle.m4)
m4_include(m4/java.m4)
+m4_include(m4/ldap.m4)
AC_INIT([GNU Data Access],[major.minor.micro],[https://bugzilla.gnome.org/enter_bug.cgi?product=libgda],[libgda],[http://www.gnome-db.org])
AC_PREREQ([2.67])
@@ -287,7 +288,7 @@ dnl ******************************
dnl linklibext is the shared link library extension, which varies by platform
EXPORT_SYM_REGEX='-export-symbols-regex "^(gda_|fnYM49765777344607__gda).*"'
-EXPORT_PROV_SYM_REGEX='-export-symbols-regex "^(plugin_|Java_|g_module).*"'
+EXPORT_PROV_SYM_REGEX='-export-symbols-regex "^(plugin_|Java_|g_module|gdaprov_).*"'
EXPORT_UI_SYM_REGEX='-export-symbols-regex "^(gdaui_).*"'
AC_MSG_CHECKING([for platform])
platform_win32=no
@@ -551,6 +552,9 @@ ORACLE_CHECK($lib)
dnl Test for JAVA and JDBC
JAVA_CHECK()
+dnl Test for LDAP
+LDAP_CHECK($lib)
+
dnl test for FireBird
try_firebird=true
AC_ARG_WITH(firebird,
@@ -816,6 +820,7 @@ libgda/providers-support/Makefile
libgda/sql-parser/Makefile
libgda/sqlite/Makefile
libgda/sqlite/sqlite-src/Makefile
+libgda/sqlite/virtual/libgda-virtual.h
libgda/sqlite/virtual/Makefile
libgda/thread-wrapper/Makefile
providers/Makefile
@@ -839,6 +844,8 @@ providers/sqlite/Makefile
providers/sqlite/libgda-sqlite-5.0.pc
providers/jdbc/Makefile
providers/jdbc/libgda-jdbc-5.0.pc
+providers/ldap/Makefile
+providers/ldap/libgda-ldap-5.0.pc
providers/web/Makefile
providers/web/libgda-web-5.0.pc
providers/skel-implementation/Makefile
@@ -873,6 +880,7 @@ tools/browser/common/Makefile
tools/browser/schema-browser/Makefile
tools/browser/query-exec/Makefile
tools/browser/data-manager/Makefile
+tools/browser/ldap-browser/Makefile
tools/browser/dummy-perspective/Makefile
tools/browser/canvas/Makefile
tools/browser/doc/Makefile
@@ -938,6 +946,7 @@ echo " SQLite = yes `if test x$have_sqlite = xyes; then echo '(from system
echo " SQLCipher = `if test x$enable_crypto != xyes; then echo no; else echo yes; fi`"
echo " JDBC = $java_found"
echo " WEB = `if test x$have_libsoup = xyes; then echo yes; else echo no; fi`"
+echo " LDAP = `if test x$ldap_found = xyes; then echo yes; else echo no; fi`"
if test x"$br_cv_binreloc" != "xyes" -a x"$platform_win32" != "xyes"
then
echo " Binreloc support is disabled: Libgda will not be relocatable. To enable binreloc support re-run with --enable-binreloc (see http://autopackage.org/docs/binreloc for more information)"
diff --git a/doc/C/libgda-5.0-docs.sgml b/doc/C/libgda-5.0-docs.sgml
index 7f395c5..c81d4cb 100644
--- a/doc/C/libgda-5.0-docs.sgml
+++ b/doc/C/libgda-5.0-docs.sgml
@@ -559,6 +559,7 @@
<xi:include href="xml/gda-virtual-connection.xml"/>
<xi:include href="xml/gda-vconnection-data-model.xml"/>
<xi:include href="xml/gda-vconnection-hub.xml"/>
+ <xi:include href="xml/gda-ldap-connection.xml"/>
</chapter>
<chapter id="data_models">
@@ -615,6 +616,7 @@
<xi:include href="xml/gda-data-access-wrapper.xml"/>
<xi:include href="xml/gda-data-model-array.xml"/>
<xi:include href="xml/gda-row.xml"/>
+ <xi:include href="xml/gda-data-model-ldap.xml"/>
<xi:include href="xml/gda-data-model-bdb.xml"/>
<xi:include href="xml/gda-data-model-dir.xml"/>
<xi:include href="xml/gda-data-proxy.xml"/>
@@ -783,6 +785,7 @@ g_object_unref (tree);
<xi:include href="xml/gda-tree-mgr-schemas.xml"/>
<xi:include href="xml/gda-tree-mgr-tables.xml"/>
<xi:include href="xml/gda-tree-mgr-columns.xml"/>
+ <xi:include href="xml/gda-tree-mgr-ldap.xml"/>
</chapter>
<chapter id="data_conv">
@@ -1705,6 +1708,18 @@ g_object_unref (eng);
<index id="index-4-2-4" role="4.2.4">
<title>Index of new symbols in 4.2.4</title>
</index>
+ <index id="index-4-2-5" role="4.2.5">
+ <title>Index of new symbols in 4.2.5</title>
+ </index>
+ <index id="index-4-2-6" role="4.2.6">
+ <title>Index of new symbols in 4.2.6</title>
+ </index>
+ <index id="index-4-2-7" role="4.2.7">
+ <title>Index of new symbols in 4.2.7</title>
+ </index>
+ <index id="index-4-2-8" role="4.2.8">
+ <title>Index of new symbols in 4.2.8</title>
+ </index>
<index id="index-5-0" role="5.0">
<title>Index of new symbols in 5.0</title>
</index>
diff --git a/doc/C/libgda-sections.txt b/doc/C/libgda-sections.txt
index a01dad4..8be9a5b 100644
--- a/doc/C/libgda-sections.txt
+++ b/doc/C/libgda-sections.txt
@@ -33,6 +33,7 @@ GDA_ATTRIBUTE_AUTO_INCREMENT
GDA_ATTRIBUTE_NUMERIC_PRECISION
GDA_ATTRIBUTE_NUMERIC_SCALE
GDA_ATTRIBUTE_IS_DEFAULT
+GDA_ATTRIBUTE_TREE_NODE_UNKNOWN_CHILDREN
<SUBSECTION>
GdaAttributesManager
GdaAttributesManagerSignal
@@ -218,6 +219,70 @@ gda_data_model_bdb_get_type
</SECTION>
<SECTION>
+<FILE>gda-data-model-ldap</FILE>
+<TITLE>GdaDataModelLdap</TITLE>
+GdaDataModelLdap
+GdaDataModelLdapClass
+GdaDataModelLdapPrivate
+GdaLdapSearchScope
+gda_data_model_ldap_new
+gda_data_model_ldap_compute_columns
+<SUBSECTION Standard>
+GDA_DATA_MODEL_LDAP
+GDA_DATA_MODEL_LDAP_CLASS
+GDA_IS_DATA_MODEL_LDAP
+GDA_IS_DATA_MODEL_LDAP_CLASS
+GDA_TYPE_DATA_MODEL_LDAP
+gda_data_model_ldap_get_type
+</SECTION>
+
+<SECTION>
+<FILE>gda-ldap-connection</FILE>
+<TITLE>GdaLdapConnection</TITLE>
+<INCLUDE>virtual/gda-ldap-connection.h</INCLUDE>
+GdaLdapConnection
+GdaLdapConnectionClass
+GdaLdapConnectionPrivate
+gda_ldap_connection_get_base_dn
+gda_ldap_connection_declare_table
+gda_ldap_connection_undeclare_table
+<SUBSECTION>
+gda_ldap_dn_split
+GdaLdapAttribute
+GdaLdapEntry
+gda_ldap_is_dn
+gda_ldap_dn_split
+gda_ldap_describe_entry
+gda_ldap_entry_free
+gda_ldap_get_entry_children
+<SUBSECTION>
+GdaLdapClassKind
+GdaLdapClass
+gda_ldap_get_class_info
+gda_ldap_get_top_classes
+<SUBSECTION Standard>
+GDA_LDAP_CONNECTION
+GDA_LDAP_CONNECTION_CLASS
+GDA_IS_LDAP_CONNECTION
+GDA_IS_LDAP_CONNECTION_CLASS
+GDA_TYPE_LDAP_CONNECTION
+gda_ldap_connection_get_type
+</SECTION>
+
+<SECTION>
+<FILE>gda-tree-mgr-ldap</FILE>
+<TITLE>GdaTreeMgrLdap</TITLE>
+GdaTreeMgrLdap
+gda_tree_mgr_ldap_new
+<SUBSECTION Standard>
+GDA_TREE_MGR_LDAP
+GDA_TREE_MGR_LDAP_GET_CLASS
+GDA_IS_TREE_MGR_LDAP
+GDA_TYPE_TREE_MGR_LDAP
+gda_tree_mgr_ldap_get_type
+</SECTION>
+
+<SECTION>
<FILE>gda-data-model-dir</FILE>
<TITLE>GdaDataModelDir</TITLE>
GdaDataModelDir
@@ -1570,6 +1635,7 @@ gda_tree_add_manager
gda_tree_clean
gda_tree_update_all
gda_tree_update_part
+gda_tree_update_children
gda_tree_get_nodes_in_path
gda_tree_get_node
gda_tree_get_node_path
diff --git a/doc/C/libgda-ui-sections.txt b/doc/C/libgda-ui-sections.txt
index 2fa2538..21cae09 100644
--- a/doc/C/libgda-ui-sections.txt
+++ b/doc/C/libgda-ui-sections.txt
@@ -281,6 +281,8 @@ gdaui_server_operation_get_type
GdauiTreeStore
gdaui_tree_store_new
gdaui_tree_store_newv
+gdaui_tree_store_get_node
+gdaui_tree_store_get_iter
<SUBSECTION Standard>
GDAUI_TREE_STORE
GDAUI_IS_TREE_STORE
diff --git a/doc/C/libgda.types.in b/doc/C/libgda.types.in
index 421edb3..1e81e43 100644
--- a/doc/C/libgda.types.in
+++ b/doc/C/libgda.types.in
@@ -21,6 +21,9 @@ gda_handler_bin_get_type
gda_handler_type_get_type
gda_data_model_array_get_type
@LIBGDA_BDB_TYPE@
+ LIBGDA_LDAP_TYPE@
+ LIBGDA_LDAP_TYPE2@
+ LIBGDA_LDAP_TYPE3@
gda_data_model_dir_get_type
gda_row_get_type
gda_data_model_get_type
diff --git a/doc/C/limitations.xml b/doc/C/limitations.xml
index 066a7a4..b65118e 100644
--- a/doc/C/limitations.xml
+++ b/doc/C/limitations.xml
@@ -163,6 +163,12 @@
</para>
</sect1>
+ <sect1 id="limitations_ldap"><title>For LDAP</title>
+ <para>
+ TODO.
+ </para>
+ </sect1>
+
<sect1 id="limitations_jdbc"><title>For JDBC based providers</title>
<para>
The following limitations apply to databases accessed via Libgda through a JDBC driver. When loading
diff --git a/doc/C/prov-notes.xml b/doc/C/prov-notes.xml
index 498d598..27eb5a0 100644
--- a/doc/C/prov-notes.xml
+++ b/doc/C/prov-notes.xml
@@ -263,5 +263,29 @@ Opening connection 'c0' for: SQLCipher://DB_NAME=testcrypt
and <link linkend="limitations_sqlcipher">SQLCipher provider's limitations</link>.
</para>
</sect1>
+
+ <sect1 id="provider_notes_ldap"><title>For LDAP</title>
+ <para>
+ The LDAP provider maps LDAP searches to &LIBGDA;'s data models, with the following design choices:
+ <itemizedlist>
+ <listitem><para>A data model column is created for each attibute the LDAP search returns, plus one
+ column for the DN (Distinguished name), as the 1st column of each search;
+ so if no attribute is requested, the resulting data model
+ will only contain one column for the DN</para></listitem>
+ <listitem><para>If not otherwise specified, the data type of each data model column is determined
+ by the data type of the corresponding column attribute</para></listitem>
+ <listitem><para>Multi valued attributes are by default handled as an invalid data, but it is possible to
+ specify instead to report a NULL value, or an array in a CSV notation.</para></listitem>
+ <listitem><para>For performances reasons, some data is cached (unless the "USE_CACHE" connection
+ variable is set to FALSE). Cache files are in the users's home directory, as per the
+ <ulink url="http://www.freedesktop.org/wiki/Specifications/basedir-spec">XDG Base Directory Specification</ulink></para></listitem>
+ </itemizedlist>
+ </para>
+ <para>
+ For more information, see the <link linkend="GdaDataModelLdap">GdaDataModelLdap</link> and
+ the <link linkend="GdaLdapConnection">GdaLdapConnection</link> objects.
+ </para>
+ </sect1>
+
</chapter>
diff --git a/installers/Windows/etc/gtk-2.0/gtkrc b/installers/Windows/etc/gtk-3.0/gtkrc
similarity index 100%
rename from installers/Windows/etc/gtk-2.0/gtkrc
rename to installers/Windows/etc/gtk-3.0/gtkrc
diff --git a/installers/Windows/gda-browser.nsi b/installers/Windows/gda-browser.nsi
index 974f6a4..0b7ba5d 100644
--- a/installers/Windows/gda-browser.nsi
+++ b/installers/Windows/gda-browser.nsi
@@ -18,6 +18,7 @@ SetCompressor lzma
!include "prov_mysql.nsh"
!include "prov_postgresql.nsh"
!include "prov_web.nsh"
+!include "prov_ldap.nsh"
!include "prov_mdb.nsh"
!include "prov_oracle.nsh"
!include "prov_sqlite.nsh"
@@ -117,6 +118,8 @@ LangString DESC_prov_sqlite ${LANG_ENGLISH} "Sqlite database provider"
LangString DESC_prov_sqlite ${LANG_FRENCH} "Fournisseur pour les bases de données Sqlite"
LangString DESC_prov_web ${LANG_ENGLISH} "Provider for database accessed through a web server"
LangString DESC_prov_web ${LANG_FRENCH} "Fournisseur pour les bases de données via un serveur web"
+LangString DESC_prov_ldap ${LANG_ENGLISH} "Provider for LDAP directory"
+LangString DESC_prov_ldap ${LANG_FRENCH} "Fournisseur pour les répertoires LDAP"
; Section descriptions
@@ -129,6 +132,7 @@ LangString DESC_prov_web ${LANG_FRENCH} "Fournisseur pour les bases de donn
!insertmacro MUI_DESCRIPTION_TEXT ${SEC06} $(DESC_prov_oracle)
!insertmacro MUI_DESCRIPTION_TEXT ${SEC07} $(DESC_prov_sqlite)
!insertmacro MUI_DESCRIPTION_TEXT ${SEC08} $(DESC_prov_web)
+ !insertmacro MUI_DESCRIPTION_TEXT ${SEC09} $(DESC_prov_ldap)
!insertmacro MUI_FUNCTION_DESCRIPTION_END
diff --git a/installers/Windows/make-zip-setup.sh b/installers/Windows/make-zip-setup.sh
index af46d38..16edc76 100755
--- a/installers/Windows/make-zip-setup.sh
+++ b/installers/Windows/make-zip-setup.sh
@@ -31,13 +31,13 @@ current_dir=`pwd`
archive=${current_dir}/libgda-${version}.zip
archive_dev=${current_dir}/libgda-dev-${version}.zip
archive_ext=${current_dir}/libgda-dep-${version}.zip
-nshfiles=(core.nsh prov_bdb.nsh prov_mdb.nsh prov_mysql.nsh prov_oracle.nsh prov_postgresql.nsh prov_sqlite.nsh prov_web.nsh)
+nshfiles=(core.nsh prov_bdb.nsh prov_mdb.nsh prov_mysql.nsh prov_oracle.nsh prov_postgresql.nsh prov_sqlite.nsh prov_web.nsh prov_ldap.nsh)
# remove current archive if it exists
rm -f $archive $archive_dev $archive_ext
rm -f *.nsh *.exe
-if test $CLEAN == "yes"
+if test "$CLEAN" = "yes"
then
exit 0
fi
@@ -213,6 +213,12 @@ Section /o "Web" SEC08
SetOverwrite try
EOF
+cat > prov_ldap.nsh <<EOF
+Section "Ldap" SEC09
+ SetOutPath "\$INSTDIR\bin"
+ SetOverwrite try
+EOF
+
cat > config.nsh <<EOF
!define PRODUCT_VERSION "$version"
EOF
@@ -240,7 +246,7 @@ EOF
#
# dependencies DLLs
#
-files=(Pathplan.dll ltdl.dll libexpat*.dll intl.dll libgio-2.*.dll libglib-2.*.dll libgmodule-2.*.dll libgobject-2.*.dll libgthread-2.*.dll libxml2*.dll zlib1.dll libsoup-2.*.dll libgdk_pixbuf-2.*.dll libgdk-win32-2.*.dll libgtk-win32-2.*.dll libatk-1.*.dll libpng*.dll libpango-1.*.dll libpangocairo-1.*.dll libpangoft2-1.*.dll libpangowin32-1.*.dll libcairo-2.dll libfontconfig-1.dll libgoocanvas-*.dll libgtksourceview-2.0-0.dll cdt.dll graph.dll gvc.dll freetype6.dll)
+files=(Pathplan.dll ltdl.dll libexpat*.dll libgio-2.*.dll libglib-2.*.dll libgmodule-2.*.dll libgobject-2.*.dll libgthread-2.*.dll libxml2*.dll libsoup-2.*.dll libgdk_pixbuf-2.*.dll libgdk-3-0.dll libgtk-3-0.dll libatk-1.*.dll libpng*.dll libpango-1.*.dll libpangocairo-1.*.dll libpangoft2-1.*.dll libpangowin32-1.*.dll libcairo-2.dll libcairo-gobject-2.dll libfontconfig-1.dll libgoocanvas-*.dll cdt.dll graph.dll gvc.dll libfreetype-6.dll libintl-8.dll libpixman-1-0.dll libjasper-1.dll libjpeg-8.dll libtiff-3.dll)
add_files_to_zip $archive_ext ${depend_path}/gtk bin $files
add_files_to_nsh core ${depend_path}/gtk bin $files
@@ -252,10 +258,13 @@ files=(libmySQL.dll)
add_files_to_zip $archive_ext ${depend_path}/mysql bin $files
add_files_to_nsh prov_mysql ${depend_path}/mysql bin $files
-files=(iconv.dll libeay32.dll libiconv-2.dll libintl-8.dll libpq.dll libxml2.dll libxslt.dll msvcr71.dll ssleay32.dll)
+files=(iconv.dll libeay32.dll libiconv-2.dll libpq.dll libxml2.dll libxslt.dll msvcr71.dll ssleay32.dll)
add_files_to_zip $archive_ext ${depend_path}/pgsql bin $files
add_files_to_nsh prov_postgresql ${depend_path}/pgsql bin $files
+files=(liblber.dll libldap.dll)
+add_files_to_zip $archive_ext ${depend_path}/ldap bin $files
+add_files_to_nsh prov_ldap ${depend_path}/ldap bin $files
#
# dependencies from the cross compilation environment
@@ -298,6 +307,9 @@ files=(oracle_specs_dsn.xml oracle_specs_create_table.xml)
add_files_to_zip $archive $prefix share/libgda-5.0 $files
add_files_to_nsh prov_oracle $prefix share/libgda-5.0 $files
+files=(ldap_specs_auth.xml ldap_specs_dsn.xml)
+add_files_to_zip $archive $prefix share/libgda-5.0 $files
+add_files_to_nsh prov_ldap $prefix share/libgda-5.0 $files
files=(gdaui-generic.png)
add_files_to_zip $archive $prefix share/libgda-5.0/pixmaps $files
@@ -374,16 +386,16 @@ add_files_to_zip $archive $prefix etc/libgda-5.0 $files
add_files_to_nsh core $prefix etc/libgda-5.0 $files
files=(gdk-pixbuf.loaders gtk.immodules)
-add_files_to_zip $archive_ext $cross_path etc/gtk-2.0 $files
-add_files_to_nsh core $cross_path etc/gtk-2.0 $files
+add_files_to_zip $archive_ext $cross_path etc/gtk-3.0 $files
+add_files_to_nsh core $cross_path etc/gtk-3.0 $files
files=(gtkrc)
-add_files_to_zip $archive_ext . etc/gtk-2.0 $files
+add_files_to_zip $archive_ext . etc/gtk-3.0 $files
add_files_to_nsh core . etc/gtk-2.0 $files
-files=(pango.modules)
-add_files_to_zip $archive_ext $cross_path etc/pango $files
-add_files_to_nsh core $cross_path etc/pango $files
+#files=(pango.modules)
+#add_files_to_zip $archive_ext $cross_path etc/pango $files
+#add_files_to_nsh core $cross_path etc/pango $files
files=(gda-sql-5.0.exe libgda-5.0-4.dll libgda-report-5.0-4.dll libgda-ui-5.0-4.dll gda-browser-5.0.exe gda-control-center-5.0.exe)
add_files_to_zip $archive $prefix bin $files
@@ -423,6 +435,10 @@ files=(libgda-web.dll)
add_files_to_zip $archive $prefix lib/libgda-5.0/providers $files
add_files_to_nsh prov_web $prefix lib/libgda-5.0/providers $files
+files=(libgda-ldap.dll)
+add_files_to_zip $archive $prefix lib/libgda-5.0/providers $files
+add_files_to_nsh prov_ldap $prefix lib/libgda-5.0/providers $files
+
files=(libgda-oracle.dll)
add_files_to_zip $archive $prefix lib/libgda-5.0/providers $files
add_files_to_nsh prov_oracle $prefix lib/libgda-5.0/providers $files
@@ -431,9 +447,9 @@ files=(gdaui-entry-filesel-spec.xml gdaui-entry-password.xml gdaui-entry-pict-sp
add_files_to_zip $archive $prefix lib/libgda-5.0/plugins $files
add_files_to_nsh core $prefix lib/libgda-5.0/plugins $files
-files=(libpixmap.dll libwimp.dll)
-add_files_to_zip $archive_ext $cross_path lib/gtk-2.0/2.10.0/engines $files
-add_files_to_nsh core $cross_path lib/gtk-2.0/2.10.0/engines $files
+files=(libwimp.dll)
+add_files_to_zip $archive_ext $cross_path lib/gtk-3.0/3.0.0/engines $files
+add_files_to_nsh core $cross_path lib/gtk-3.0/3.0.0/engines $files
#
# includes
@@ -473,7 +489,7 @@ add_all_files_to_zip $archive_dev $prefix lib/pkgconfig
#
# static libs
#
-files=(libgda-5.0.a libgda-5.0.dll.a libgda-5.0.lib libgda-5.0.def libgda-report-5.0.a libgda-report-5.0.dll.a libgda-report-5.0.lib libgda-report-5.0.def libgda-ui-5.0.a libgda-ui-5.0.dll.a libgda-ui-5.0.lib libgda-ui-5.0.def)
+files=(libgda-5.0.dll.a libgda-5.0.lib libgda-5.0.def libgda-report-5.0.dll.a libgda-report-5.0.lib libgda-report-5.0.def libgda-ui-5.0.dll.a libgda-ui-5.0.lib libgda-ui-5.0.def)
add_files_to_zip $archive_dev $prefix lib $files
#
@@ -485,7 +501,7 @@ add_files_to_zip $archive_dev $prefix share/libgda-5.0/demo $files
#
# doc
#
-add_all_files_to_zip $archive_dev $prefix share/gtk-doc/html/libgda-5.0
+#add_all_files_to_zip $archive_dev $prefix share/gtk-doc/html/libgda-5.0
#
# translations
diff --git a/libgda-report/engine/Makefile.am b/libgda-report/engine/Makefile.am
index 8d34633..8db8588 100644
--- a/libgda-report/engine/Makefile.am
+++ b/libgda-report/engine/Makefile.am
@@ -8,6 +8,7 @@ AM_CPPFLAGS = \
-I$(top_srcdir) \
-I$(top_srcdir)/libgda \
-I$(top_srcdir)/libgda/sqlite \
+ -I$(top_builddir)/libgda/sqlite \
$(COREDEPS_CFLAGS) \
$(GDKPIXBUF_CFLAGS) \
$(COREDEPS_WFLAGS)
diff --git a/libgda-ui/gdaui-tree-store.c b/libgda-ui/gdaui-tree-store.c
index f8fb59d..afbaf42 100644
--- a/libgda-ui/gdaui-tree-store.c
+++ b/libgda-ui/gdaui-tree-store.c
@@ -1,5 +1,8 @@
-/*
- * Copyright (C) 2009 Vivien Malerba <malerba gnome-db org>
+/*
+ * Copyright (C) 2009 - 2011 The GNOME Foundation
+ *
+ * AUTHORS:
+ * Vivien Malerba <malerba gnome-db org>
*
* This Library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License as
@@ -23,6 +26,7 @@
#include <libgda/gda-tree-node.h>
#include <libgda/gda-tree-manager.h>
#include <libgda/gda-value.h>
+#include <libgda/gda-attributes-manager.h>
#include <gtk/gtk.h>
#include "marshallers/gdaui-marshal.h"
@@ -39,6 +43,8 @@ static void gdaui_tree_store_get_property (GObject *object,
GValue *value,
GParamSpec *pspec);
+#define NOT_A_NODE ((GdaTreeNode*) 0x01)
+
/* signals */
enum
{
@@ -366,9 +372,9 @@ gdaui_tree_store_set_property (GObject *object,
static void
gdaui_tree_store_get_property (GObject *object,
- guint param_id,
- GValue *value,
- GParamSpec *pspec)
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec)
{
GdauiTreeStore *store;
@@ -438,7 +444,7 @@ gdaui_tree_store_new (GdaTree *tree, guint n_columns, ...)
}
/**
- * gdaui_tree_store_newv
+ * gdaui_tree_store_newv:
* @tree: a #GdaTree object
* @n_columns: number of columns in the tree store
* @types: an array of @n_columns GType to specify the type of each column
@@ -486,10 +492,81 @@ gdaui_tree_store_newv (GdaTree *tree, guint n_columns, GType *types, const gchar
*
* REM about the GtkTreeIter:
* iter->user_data <==> GdaTreeNode for the row
- * iter->user_data2 <==> Next GdaTreeNode
+ * iter->user_data2 <==> parent GdaTreeNode if @user_data == NOT_A_NODE
* iter->stamp is reset any time the model changes, 0 means invalid iter
*/
+/**
+ * gdaui_tree_store_get_node:
+ * @store: a #GdauiTreeStore object
+ * @iter: a valid #GtkTreeIter
+ *
+ * Get the #GdaTreeNode represented by @iter.
+ *
+ * Returns: (transfer none): the #GdaTreeNode represented by @iter, or %NULL if an error occurred
+ *
+ * Since: 4.2.8
+ */
+GdaTreeNode *
+gdaui_tree_store_get_node (GdauiTreeStore *store, GtkTreeIter *iter)
+{
+ g_return_val_if_fail (GDAUI_IS_TREE_STORE (store), NULL);
+ g_return_val_if_fail (iter, NULL);
+ g_return_val_if_fail (iter->stamp == store->priv->stamp, NULL);
+
+ GdaTreeNode *node;
+ node = (GdaTreeNode*) iter->user_data;
+ if (node == NOT_A_NODE)
+ return NULL;
+ return node;
+}
+
+
+/**
+ * gdaui_tree_store_get_iter:
+ * @store: a #GdauiTreeStore object
+ * @iter: a pointer to a #GtkTreeIter
+ * @node: a #GdaTreeNode in @store
+ *
+ * Sets @iter to represent @node in the tree.
+ *
+ * Returns: %TRUE if no error occurred and @iter is valid
+ *
+ * Since: 4.2.8
+ */
+gboolean
+gdaui_tree_store_get_iter (GdauiTreeStore *store, GtkTreeIter *iter, GdaTreeNode *node)
+{
+ g_return_val_if_fail (GDAUI_IS_TREE_STORE (store), FALSE);
+ g_return_val_if_fail (GDA_IS_TREE_NODE (node), FALSE);
+
+ GdaTreeNode *parent = NULL;
+ GSList *rootnodes;
+
+ rootnodes = gda_tree_get_nodes_in_path (store->priv->tree, NULL, FALSE);
+ if (rootnodes) {
+ for (parent = node;
+ parent;
+ parent = gda_tree_node_get_parent (parent)) {
+ if (g_slist_find (rootnodes, parent))
+ break;
+ }
+ g_slist_free (rootnodes);
+ }
+
+ iter->user_data2 = NULL;
+ if (parent) {
+ iter->stamp = store->priv->stamp;
+ iter->user_data = (gpointer) node;
+ return TRUE;
+ }
+ else {
+ iter->stamp = 0;
+ iter->user_data = NULL;
+ return FALSE;
+ }
+}
+
static GtkTreeModelFlags
tree_store_get_flags (GtkTreeModel *tree_model)
{
@@ -552,12 +629,37 @@ tree_store_get_iter (GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreePath *p
if (node) {
iter->stamp = store->priv->stamp;
iter->user_data = (gpointer) node;
+ iter->user_data2 = NULL;
return TRUE;
}
else {
+ GtkTreePath *path2;
+ path2 = gtk_tree_path_copy (path);
+ if (gtk_tree_path_up (path2)) {
+ path_str = gtk_tree_path_to_string (path2);
+ node = gda_tree_get_node (store->priv->tree, path_str, FALSE);
+ /*g_print ("Path2 %s => node %p\n", path_str, node);*/
+ g_free (path_str);
+ }
+ gtk_tree_path_free (path2);
+
+ if (node) {
+ const GValue *cv;
+ cv = gda_tree_node_get_node_attribute (node,
+ GDA_ATTRIBUTE_TREE_NODE_UNKNOWN_CHILDREN);
+ if (cv && (G_VALUE_TYPE (cv) == G_TYPE_BOOLEAN) &&
+ g_value_get_boolean (cv)) {
+ iter->stamp = store->priv->stamp;
+ iter->user_data = (gpointer) NOT_A_NODE;
+ iter->user_data2 = (gpointer) node;
+ return TRUE;
+ }
+ }
+
iter->stamp = 0;
iter->user_data = NULL;
- return FALSE;
+ iter->user_data2 = NULL;
+ return FALSE;
}
}
@@ -565,17 +667,33 @@ static GtkTreePath *
tree_store_get_path (GtkTreeModel *tree_model, GtkTreeIter *iter)
{
GdauiTreeStore *store;
- GtkTreePath *path;
- gchar *path_str;
+ GtkTreePath *path = NULL;
+ gchar *path_str = NULL;
+ GdaTreeNode *node;
g_return_val_if_fail (GDAUI_IS_TREE_STORE (tree_model), NULL);
store = GDAUI_TREE_STORE (tree_model);
g_return_val_if_fail (iter, NULL);
g_return_val_if_fail (iter->stamp == store->priv->stamp, NULL);
- path_str = gda_tree_get_node_path (store->priv->tree, GDA_TREE_NODE (iter->user_data));
+ node = (GdaTreeNode*) iter->user_data;
+ if (node == NOT_A_NODE) {
+ GtkTreeIter iter2;
+ gchar *tmp;
+
+ iter2 = *iter;
+ g_assert (gtk_tree_model_iter_parent (tree_model, &iter2, iter));
+ path_str = gda_tree_get_node_path (store->priv->tree,
+ (GdaTreeNode*) iter2.user_data);
+ tmp = g_strdup_printf ("%s:0", path_str);
+ g_free (path_str);
+ path_str = tmp;
+ }
+ else
+ path_str = gda_tree_get_node_path (store->priv->tree, node);
+
/*g_print ("Node %p => path %s\n", iter->user_data, path_str);*/
- path = gtk_tree_path_new_from_string (path_str);
+ path = gtk_tree_path_new_from_string (path_str);
g_free (path_str);
return path;
@@ -585,8 +703,9 @@ static void
tree_store_get_value (GtkTreeModel *tree_model, GtkTreeIter *iter, gint column, GValue *value)
{
GdauiTreeStore *store;
- const GValue *tmp;
+ const GValue *tmp = NULL;
ColumnSpec *cs;
+ GdaTreeNode *node;
g_return_if_fail (GDAUI_IS_TREE_STORE (tree_model));
store = GDAUI_TREE_STORE (tree_model);
@@ -603,7 +722,11 @@ tree_store_get_value (GtkTreeModel *tree_model, GtkTreeIter *iter, gint column,
gda_value_set_null (value);
return;
}
- tmp = gda_tree_node_fetch_attribute (GDA_TREE_NODE (iter->user_data), cs->attribute_name);
+
+ node = (GdaTreeNode*) iter->user_data;
+ if (node != NOT_A_NODE)
+ tmp = gda_tree_node_fetch_attribute (node, cs->attribute_name);
+
if (!tmp) {
g_value_init (value, cs->type);
return;
@@ -626,7 +749,7 @@ tree_store_iter_next (GtkTreeModel *tree_model, GtkTreeIter *iter)
{
GdauiTreeStore *store;
GdaTreeNode *parent;
- GSList *list, *current;
+ GdaTreeNode *node;
g_return_val_if_fail (GDAUI_IS_TREE_STORE (tree_model), FALSE);
store = GDAUI_TREE_STORE (tree_model);
@@ -634,71 +757,99 @@ tree_store_iter_next (GtkTreeModel *tree_model, GtkTreeIter *iter)
g_return_val_if_fail (iter, FALSE);
g_return_val_if_fail (iter->stamp == store->priv->stamp, FALSE);
- parent = gda_tree_node_get_parent (GDA_TREE_NODE (iter->user_data));
- if (parent)
- list = gda_tree_node_get_children (parent);
- else
- list = gda_tree_get_nodes_in_path (store->priv->tree, NULL, FALSE);
-
- current = g_slist_find (list, iter->user_data);
- g_assert (current);
- if (current->next) {
-#ifdef GDA_DEBUG_NO
-#define GDA_ATTRIBUTE_NAME "__gda_attr_name"
- g_print ("Next %s(%p) => %s(%p)\n",
- gda_value_stringify (gda_tree_node_fetch_attribute (GDA_TREE_NODE (iter->user_data), GDA_ATTRIBUTE_NAME)),
- iter->user_data,
- gda_value_stringify (gda_tree_node_fetch_attribute (GDA_TREE_NODE (current->next->data), GDA_ATTRIBUTE_NAME)),
- current->next->data);
-#endif
- iter->user_data = (gpointer) current->next->data;
- g_slist_free (list);
- return TRUE;
- }
- else {
+ node = (GdaTreeNode*) iter->user_data;
+ if (node == NOT_A_NODE) {
iter->stamp = 0;
iter->user_data = NULL;
- g_slist_free (list);
+ iter->user_data2 = NULL;
return FALSE;
}
+ else {
+ GSList *list, *current;
+ parent = gda_tree_node_get_parent (node);
+ if (parent)
+ list = gda_tree_node_get_children (parent);
+ else
+ list = gda_tree_get_nodes_in_path (store->priv->tree, NULL, FALSE);
+
+ current = g_slist_find (list, iter->user_data);
+ g_assert (current);
+ if (current->next) {
+#ifdef GDA_DEBUG_NO
+#define GDA_ATTRIBUTE_NAME "__gda_attr_name"
+ g_print ("Next %s(%p) => %s(%p)\n",
+ gda_value_stringify (gda_tree_node_fetch_attribute (GDA_TREE_NODE (iter->user_data), GDA_ATTRIBUTE_NAME)),
+ iter->user_data,
+ gda_value_stringify (gda_tree_node_fetch_attribute (GDA_TREE_NODE (current->next->data), GDA_ATTRIBUTE_NAME)),
+ current->next->data);
+#endif
+ iter->user_data = (gpointer) current->next->data;
+ iter->user_data2 = NULL;
+ g_slist_free (list);
+ return TRUE;
+ }
+ else {
+ iter->stamp = 0;
+ iter->user_data = NULL;
+ iter->user_data2 = NULL;
+ g_slist_free (list);
+ return FALSE;
+ }
+ }
}
static gboolean
tree_store_iter_children (GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *parent)
{
GdauiTreeStore *store;
- GSList *list;
+ GSList *list = NULL;
+ GdaTreeNode *node = NULL;
g_return_val_if_fail (GDAUI_IS_TREE_STORE (tree_model), FALSE);
store = GDAUI_TREE_STORE (tree_model);
g_return_val_if_fail (store->priv->tree, FALSE);
g_return_val_if_fail (iter, FALSE);
- if (!parent)
- list = gda_tree_get_nodes_in_path (store->priv->tree, NULL, FALSE);
- else {
+ if (parent) {
g_return_val_if_fail (parent->stamp == store->priv->stamp, FALSE);
- list = gda_tree_node_get_children (GDA_TREE_NODE (parent->user_data));
+ node = (GdaTreeNode*) parent->user_data;
+ if (node != NOT_A_NODE)
+ list = gda_tree_node_get_children (node);
}
+ else
+ list = gda_tree_get_nodes_in_path (store->priv->tree, NULL, FALSE);
if (list) {
iter->stamp = store->priv->stamp;
iter->user_data = (gpointer) list->data;
+ iter->user_data2 = NULL;
g_slist_free (list);
return TRUE;
}
- else {
- iter->stamp = 0;
- iter->user_data = NULL;
- return FALSE;
+ else if (node != NOT_A_NODE) {
+ const GValue *cv;
+ cv = gda_tree_node_get_node_attribute (node,
+ GDA_ATTRIBUTE_TREE_NODE_UNKNOWN_CHILDREN);
+ if (cv && (G_VALUE_TYPE (cv) == G_TYPE_BOOLEAN) &&
+ g_value_get_boolean (cv)) {
+ iter->stamp = store->priv->stamp;
+ iter->user_data = (gpointer) NOT_A_NODE;
+ iter->user_data2 = (gpointer) node;
+ return TRUE;
+ }
}
+
+ iter->stamp = 0;
+ iter->user_data = NULL;
+ iter->user_data2 = NULL;
+ return FALSE;
}
static gboolean
tree_store_iter_has_child (GtkTreeModel *tree_model, GtkTreeIter *iter)
{
GdauiTreeStore *store;
- GSList *list;
+ GdaTreeNode *node;
g_return_val_if_fail (GDAUI_IS_TREE_STORE (tree_model), FALSE);
store = GDAUI_TREE_STORE (tree_model);
@@ -706,46 +857,66 @@ tree_store_iter_has_child (GtkTreeModel *tree_model, GtkTreeIter *iter)
g_return_val_if_fail (iter, FALSE);
g_return_val_if_fail (iter->stamp == store->priv->stamp, FALSE);
- list = gda_tree_node_get_children (GDA_TREE_NODE (iter->user_data));
- if (list) {
- g_slist_free (list);
+ node = (GdaTreeNode*) iter->user_data;
+ if (node == NOT_A_NODE)
+ return FALSE;
+ if (gda_tree_node_get_child_index (node, 0))
return TRUE;
+ else {
+ const GValue *cv;
+ cv = gda_tree_node_get_node_attribute (node,
+ GDA_ATTRIBUTE_TREE_NODE_UNKNOWN_CHILDREN);
+ if (cv && (G_VALUE_TYPE (cv) == G_TYPE_BOOLEAN) &&
+ g_value_get_boolean (cv))
+ return TRUE;
+ else
+ return FALSE;
}
- else
- return FALSE;
}
static gint
tree_store_iter_n_children (GtkTreeModel *tree_model, GtkTreeIter *iter)
{
GdauiTreeStore *store;
- GSList *list;
+ GSList *list = NULL;
+ GdaTreeNode *node = NULL;
g_return_val_if_fail (GDAUI_IS_TREE_STORE (tree_model), -1);
store = GDAUI_TREE_STORE (tree_model);
g_return_val_if_fail (store->priv->tree, 0);
- if (!iter)
- list = gda_tree_get_nodes_in_path (store->priv->tree, NULL, FALSE);
- else {
+ if (iter) {
g_return_val_if_fail (iter->stamp == store->priv->stamp, FALSE);
- list = gda_tree_node_get_children (GDA_TREE_NODE (iter->user_data));
+ node = (GdaTreeNode*) iter->user_data;
+ if (node != NOT_A_NODE)
+ list = gda_tree_node_get_children (GDA_TREE_NODE (iter->user_data));
}
+ else
+ list = gda_tree_get_nodes_in_path (store->priv->tree, NULL, FALSE);
+
if (list) {
gint retval;
retval = g_slist_length (list);
g_slist_free (list);
return retval;
}
- else
- return 0;
+ else if (node != NOT_A_NODE) {
+ const GValue *cv;
+ cv = gda_tree_node_get_node_attribute (node,
+ GDA_ATTRIBUTE_TREE_NODE_UNKNOWN_CHILDREN);
+ if (cv && (G_VALUE_TYPE (cv) == G_TYPE_BOOLEAN) &&
+ g_value_get_boolean (cv))
+ return 1;
+ }
+ return 0;
}
static gboolean
tree_store_iter_nth_child (GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *parent, gint n)
{
GdauiTreeStore *store;
- GSList *list, *current;
+ GSList *list = NULL;
+ GdaTreeNode *node = NULL;
g_return_val_if_fail (GDAUI_IS_TREE_STORE (tree_model), FALSE);
store = GDAUI_TREE_STORE (tree_model);
@@ -753,32 +924,47 @@ tree_store_iter_nth_child (GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeI
g_return_val_if_fail (iter, FALSE);
if (parent) {
g_return_val_if_fail (parent->stamp == store->priv->stamp, FALSE);
- list = gda_tree_node_get_children (GDA_TREE_NODE (parent->user_data));
+ node = (GdaTreeNode*) parent->user_data;
+ if (node != NOT_A_NODE)
+ list = gda_tree_node_get_children (node);
}
else
list = gda_tree_get_nodes_in_path (store->priv->tree, NULL, FALSE);
- current = g_slist_nth (list, n);
- if (current) {
- iter->stamp = store->priv->stamp;
- iter->user_data = (gpointer) current->data;
+ if (list) {
+ node = (GdaTreeNode*) g_slist_nth_data (list, n);
g_slist_free (list);
- return TRUE;
+ if (node) {
+ iter->stamp = store->priv->stamp;
+ iter->user_data = (gpointer) node;
+ iter->user_data2 = NULL;
+ return TRUE;
+ }
}
- else {
- if (list)
- g_slist_free (list);
- iter->stamp = 0;
- iter->user_data = NULL;
- return FALSE;
+ else if ((node != NOT_A_NODE) && (n == 0)) {
+ const GValue *cv;
+ cv = gda_tree_node_get_node_attribute (node,
+ GDA_ATTRIBUTE_TREE_NODE_UNKNOWN_CHILDREN);
+ if (cv && (G_VALUE_TYPE (cv) == G_TYPE_BOOLEAN) &&
+ g_value_get_boolean (cv)) {
+ iter->stamp = store->priv->stamp;
+ iter->user_data = (gpointer) NOT_A_NODE;
+ iter->user_data2 = (gpointer) node;
+ return TRUE;
+ }
}
+
+ iter->stamp = 0;
+ iter->user_data = NULL;
+ iter->user_data2 = NULL;
+ return FALSE;
}
static gboolean
tree_store_iter_parent (GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *child)
{
GdauiTreeStore *store;
- GdaTreeNode *parent;
+ GdaTreeNode *node, *parent;
g_return_val_if_fail (GDAUI_IS_TREE_STORE (tree_model), FALSE);
store = GDAUI_TREE_STORE (tree_model);
@@ -787,17 +973,25 @@ tree_store_iter_parent (GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter
g_return_val_if_fail (child, FALSE);
g_return_val_if_fail (child->stamp == store->priv->stamp, FALSE);
- parent = gda_tree_node_get_parent (GDA_TREE_NODE (child->user_data));
+ node = (GdaTreeNode*) child->user_data;
+ if (node == NOT_A_NODE) {
+ parent = (GdaTreeNode*) child->user_data2;
+ g_assert (GDA_IS_TREE_NODE (parent));
+ }
+ else
+ parent = gda_tree_node_get_parent (node);
+
if (parent) {
iter->stamp = store->priv->stamp;
iter->user_data = (gpointer) parent;
+ iter->user_data2 = NULL;
return TRUE;
}
- else {
- iter->stamp = 0;
- iter->user_data = NULL;
- return FALSE;
- }
+
+ iter->stamp = 0;
+ iter->user_data = NULL;
+ iter->user_data2 = NULL;
+ return FALSE;
}
@@ -817,6 +1011,7 @@ tree_node_changed_cb (GdaTree *tree, GdaTreeNode *node, GdauiTreeStore *store)
memset (&iter, 0, sizeof (GtkTreeIter));
iter.stamp = store->priv->stamp;
iter.user_data = (gpointer) node;
+ iter.user_data2 = NULL;
gtk_tree_model_row_changed (GTK_TREE_MODEL (store), path, &iter);
/*g_print ("GdauiTreeStore::changed %s (node %p)\n", gtk_tree_path_to_string (path), node);*/
@@ -839,6 +1034,7 @@ tree_node_inserted_cb (GdaTree *tree, GdaTreeNode *node, GdauiTreeStore *store)
memset (&iter, 0, sizeof (GtkTreeIter));
iter.stamp = store->priv->stamp;
iter.user_data = (gpointer) node;
+ iter.user_data2 = NULL;
gtk_tree_model_row_inserted (GTK_TREE_MODEL (store), path, &iter);
/*g_print ("GdauiTreeStore::row_inserted %s (node %p)\n", gtk_tree_path_to_string (path), node);*/
@@ -861,6 +1057,7 @@ tree_node_has_child_toggled_cb (GdaTree *tree, GdaTreeNode *node, GdauiTreeStore
memset (&iter, 0, sizeof (GtkTreeIter));
iter.stamp = store->priv->stamp;
iter.user_data = (gpointer) node;
+ iter.user_data2 = NULL;
gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (store), path, &iter);
/*g_print ("GdauiTreeStore::row_has_child %s (node %p)\n", gtk_tree_path_to_string (path), node);*/
diff --git a/libgda-ui/gdaui-tree-store.h b/libgda-ui/gdaui-tree-store.h
index 0a536d6..f71da8e 100644
--- a/libgda-ui/gdaui-tree-store.h
+++ b/libgda-ui/gdaui-tree-store.h
@@ -61,18 +61,26 @@ struct _GdauiTreeStoreClass
* @short_description: Bridge between a #GdaTree and a #GtkTreeModel
* @title: GdauiTreeStore
* @stability: Stable
- * @Image:
* @see_also: #GdaTree
*
* The #GdauiTreeStore implements the #GtkTreeModel interface required
* to display data from a #GdaTree in a #GtkTreeView widget.
+ *
+ * To allow a tree to be populated only on request (ie. when the user expands a row), each
+ * #GdaTreeNode can give the attribute named #GDA_ATTRIBUTE_TREE_NODE_UNKNOWN_CHILDREN
+ * a boolean %TRUE #GValue to tell the #GdauiTreeStore data model to artificially add
+ * a dummy child for the row corresponding to the #GdaTreeNode. Then the programmer
+ * can connect to the <link linkend="GtkTreeView-test-expand-row">GtkTreeView::test-expand-row</link>
+ * signal and update the requested children.
*/
-GType gdaui_tree_store_get_type (void) G_GNUC_CONST;
+GType gdaui_tree_store_get_type (void) G_GNUC_CONST;
-GtkTreeModel *gdaui_tree_store_new (GdaTree *tree, guint n_columns, ...);
-GtkTreeModel *gdaui_tree_store_newv (GdaTree *tree, guint n_columns,
- GType *types, const gchar **attribute_names);
+GtkTreeModel *gdaui_tree_store_new (GdaTree *tree, guint n_columns, ...);
+GtkTreeModel *gdaui_tree_store_newv (GdaTree *tree, guint n_columns,
+ GType *types, const gchar **attribute_names);
+GdaTreeNode *gdaui_tree_store_get_node (GdauiTreeStore *store, GtkTreeIter *iter);
+gboolean gdaui_tree_store_get_iter (GdauiTreeStore *store, GtkTreeIter *iter, GdaTreeNode *node);
G_END_DECLS
diff --git a/libgda-ui/libgda-ui.symbols b/libgda-ui/libgda-ui.symbols
index fd3fb72..97ec1d8 100644
--- a/libgda-ui/libgda-ui.symbols
+++ b/libgda-ui/libgda-ui.symbols
@@ -172,6 +172,8 @@
gdaui_server_operation_get_type
gdaui_server_operation_new
gdaui_server_operation_new_in_dialog
+ gdaui_tree_store_get_iter
+ gdaui_tree_store_get_node
gdaui_tree_store_get_type
gdaui_tree_store_new
gdaui_tree_store_newv
diff --git a/libgda/Makefile.am b/libgda/Makefile.am
index 0d586c3..211202d 100644
--- a/libgda/Makefile.am
+++ b/libgda/Makefile.am
@@ -4,10 +4,17 @@ lib_LTLIBRARIES = libgda-5.0.la
SUBDIRS = sqlite handlers binreloc sql-parser providers-support thread-wrapper
+DEF_FLAGS=
if BDB
GDA_BDB_H=gda-data-model-bdb.h
GDA_BDB_S=gda-data-model-bdb.c
-DEF_FLAGS=-DHAVE_BDB
+DEF_FLAGS+=-DHAVE_BDB
+endif
+
+if LDAP
+GDA_LDAP_H=gda-data-model-ldap.h gda-tree-mgr-ldap.h
+GDA_LDAP_S=gda-data-model-ldap.c gda-tree-mgr-ldap.c
+DEF_FLAGS+=-DHAVE_LDAP
endif
GLOBAL_CFLAGS = \
@@ -15,6 +22,7 @@ GLOBAL_CFLAGS = \
-I$(top_builddir) \
-I$(top_srcdir)/libgda/sqlite \
-I$(top_srcdir)/libgda \
+ -I$(top_builddir)/libgda/sqlite \
-DABI_VERSION=\""$(GDA_ABI_VERSION)"\" \
$(COREDEPS_CFLAGS) \
$(BDB_CFLAGS) \
@@ -52,6 +60,7 @@ gda_headers = \
gda-data-model-array.h \
gda-data-model.h \
$(GDA_BDB_H) \
+ $(GDA_LDAP_H) \
gda-data-model-dir.h \
gda-data-model-extra.h \
gda-data-model-import.h \
@@ -110,6 +119,7 @@ gda_sources= \
gda-data-handler.c \
gda-data-model-array.c \
$(GDA_BDB_S) \
+ $(GDA_LDAP_S) \
gda-data-model.c \
gda-data-model-dir.c \
gda-data-model-dsn-list.c \
diff --git a/libgda/gda-attributes-manager.h b/libgda/gda-attributes-manager.h
index 55a30a1..bff9799 100644
--- a/libgda/gda-attributes-manager.h
+++ b/libgda/gda-attributes-manager.h
@@ -85,6 +85,12 @@ void gda_attributes_manager_foreach (GdaAttributesManager *
*/
#define GDA_ATTRIBUTE_IS_DEFAULT "__gda_attr_is_default"
+/**
+ * GDA_ATTRIBUTE_TREE_NODE_UNKNOWN_CHILDREN:
+ * This attribute, if %TRUE specifies that a tree node may or may not have any children nodes (value has a G_TYPE_BOOLEAN type).
+ */
+#define GDA_ATTRIBUTE_TREE_NODE_UNKNOWN_CHILDREN "__gda_attr_tnuchild"
+
/**
* SECTION:gda-attributes-manager
diff --git a/libgda/gda-data-model-ldap.c b/libgda/gda-data-model-ldap.c
new file mode 100644
index 0000000..72cb987
--- /dev/null
+++ b/libgda/gda-data-model-ldap.c
@@ -0,0 +1,408 @@
+/*
+ * Copyright (C) 2011 The GNOME Foundation
+ *
+ * AUTHORS:
+ * Vivien Malerba <malerba gnome-db org>
+ *
+ * This Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+#include <libgda/gda-data-model-ldap.h>
+#include <libgda/gda-connection.h>
+#include <libgda/gda-config.h>
+#include <virtual/gda-ldap-connection.h>
+#include <gmodule.h>
+#include <glib/gi18n-lib.h>
+
+enum {
+ PROP_0,
+ PROP_CNC,
+ PROP_BASE,
+ PROP_FILTER,
+ PROP_ATTRIBUTES,
+ PROP_SCOPE
+};
+
+static void
+gda_data_model_ldap_set_property (G_GNUC_UNUSED GObject *object,
+ G_GNUC_UNUSED guint param_id,
+ G_GNUC_UNUSED const GValue *value,
+ G_GNUC_UNUSED GParamSpec *pspec)
+{
+}
+
+static void
+gda_data_model_ldap_get_property (G_GNUC_UNUSED GObject *object,
+ G_GNUC_UNUSED guint param_id,
+ G_GNUC_UNUSED GValue *value,
+ G_GNUC_UNUSED GParamSpec *pspec)
+{
+}
+
+
+static void
+dummy_gda_data_model_ldap_class_init (GdaDataModelLdapClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ /* properties */
+ object_class->set_property = gda_data_model_ldap_set_property;
+ object_class->get_property = gda_data_model_ldap_get_property;
+
+ g_object_class_install_property (object_class, PROP_CNC,
+ g_param_spec_object ("cnc", NULL, "LDAP connection",
+ GDA_TYPE_CONNECTION,
+ G_PARAM_READABLE | G_PARAM_WRITABLE |
+ G_PARAM_CONSTRUCT_ONLY));
+ g_object_class_install_property (object_class, PROP_BASE,
+ g_param_spec_string ("base", NULL, "Base DN", NULL,
+ G_PARAM_READABLE | G_PARAM_WRITABLE |
+ G_PARAM_CONSTRUCT_ONLY));
+ g_object_class_install_property (object_class, PROP_FILTER,
+ g_param_spec_string ("filter", NULL, "LDAP filter", NULL,
+ G_PARAM_READABLE | G_PARAM_WRITABLE |
+ G_PARAM_CONSTRUCT_ONLY));
+ g_object_class_install_property (object_class, PROP_ATTRIBUTES,
+ g_param_spec_string ("attributes", NULL, "LDAP attributes", NULL,
+ G_PARAM_WRITABLE |
+ G_PARAM_CONSTRUCT_ONLY));
+ g_object_class_install_property (object_class, PROP_SCOPE,
+ g_param_spec_int ("scope", NULL, "LDAP search scope",
+ GDA_LDAP_SEARCH_BASE,
+ GDA_LDAP_SEARCH_SUBTREE,
+ GDA_LDAP_SEARCH_BASE,
+ G_PARAM_WRITABLE | G_PARAM_READABLE |
+ G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+dummy_gda_data_model_ldap_data_model_init (GdaDataModelIface *iface)
+{
+ iface->i_get_n_rows = NULL;
+}
+
+static GModule *ldap_prov_module = NULL;
+
+static void
+load_ldap_module (void)
+{
+ if (ldap_prov_module)
+ return;
+
+ GdaProviderInfo *pinfo;
+ pinfo = gda_config_get_provider_info ("Ldap");
+ if (!pinfo)
+ return;
+ ldap_prov_module = g_module_open (pinfo->location, 0);
+}
+
+/**
+ * gda_data_model_ldap_get_type:
+ *
+ * Since: 4.2.8
+ */
+GType
+gda_data_model_ldap_get_type (void)
+{
+ static GType type = 0;
+ if (!type) {
+ typedef GType (*Func) (void);
+ Func func;
+
+ load_ldap_module ();
+ if (!ldap_prov_module)
+ goto dummy;
+
+ if (!g_module_symbol (ldap_prov_module, "gdaprov_data_model_ldap_get_type", (void **) &func))
+ goto dummy;
+
+ type = func ();
+ return type;
+
+ dummy:
+ if (!type) {
+ /* dummy setup to enable GIR compilation */
+ g_warning (_("Dummy GdaDataModelLdap object: if you see this message in your application "
+ "then it's likely that there is an installation problem with the "
+ "LDAP provider. In any case the GdaDataModelLdap object won't be useable."));
+ static const GTypeInfo info = {
+ sizeof (GdaDataModelLdapClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) dummy_gda_data_model_ldap_class_init,
+ NULL,
+ NULL,
+ sizeof (GdaDataModelLdap),
+ 0,
+ (GInstanceInitFunc) NULL,
+ 0
+ };
+ static const GInterfaceInfo data_model_info = {
+ (GInterfaceInitFunc) dummy_gda_data_model_ldap_data_model_init,
+ NULL,
+ NULL
+ };
+
+ if (type == 0) {
+ type = g_type_register_static (G_TYPE_OBJECT, "GdaDataModelLdap", &info, 0);
+ g_type_add_interface_static (type, GDA_TYPE_DATA_MODEL, &data_model_info);
+ }
+ }
+
+ }
+ return type;
+}
+
+/**
+ * gda_data_model_ldap_new:
+ * @cnc: an LDAP opened connection (must be a balid #GdaLdapConnection)
+ * @base_dn: (allow-none): the base DN to search on, or %NULL
+ * @filter: (allow-none): an LDAP filter, for example "(objectClass=*)"
+ * @attributes: (allow-none): the list of attributes to fetch, each in the format <attname>[::<GType>] (CSV)
+ * @scope: the search scope
+ *
+ * Creates a new #GdaDataModel object to extract some LDAP contents. The returned data model will
+ * contain one row for each LDAP entry returned by the search, and will
+ * always return the DN (Distinguished Name) of the LDAP entry as first column. Other atttibutes
+ * may be mapped to other columns, see the @attributes argument.
+ *
+ * Note that the actual LDAP search command is not executed until necessary (when using the returned
+ * data model).
+ *
+ * The @base_dn is the point in the LDAP's DIT (Directory Information Tree) from where the search will
+ * occur, for example "dc=gda,dc=org". A %NULL value indicates that the starting point for the
+ * search will be the one specified when opening the LDAP connection.
+ *
+ * The @filter argument is a valid LDAP filter string, for example "(uidNumber=1001)". If %NULL, then
+ * a default search filter of "(objectClass=*)" will be used.
+ *
+ * @attributes specifies which LDAP attributes the search must return. It is a comma separated list
+ * of attribute names, for example "uidNumber, mail, uid, jpegPhoto" (spaces between attribute names
+ * are ignored). If %NULL, then no attribute will be fetched. See gda_ldap_connection_declare_table()
+ * for more information about this argument.
+ *
+ * @scope is the scope of search specified when the LDAP search is actually executed.
+ *
+ * In case of multi valued attributes, an error will be returned when trying to read the attribute:
+ * gda_data_model_iter_get_value_at() will return %NULL when using an iterator.
+ *
+ * Returns: a new #GdaDataModel
+ *
+ * Since: 4.2.8
+ */
+GdaDataModel *
+gda_data_model_ldap_new (GdaConnection *cnc,
+ const gchar *base_dn, const gchar *filter,
+ const gchar *attributes, GdaLdapSearchScope scope)
+{
+ g_return_val_if_fail (GDA_IS_LDAP_CONNECTION (cnc), NULL);
+ return (GdaDataModel*) g_object_new (GDA_TYPE_DATA_MODEL_LDAP,
+ "cnc", cnc, "base", base_dn,
+ "filter", filter, "attributes", attributes,
+ "scope", scope, NULL);
+}
+
+/**
+ * gda_data_model_ldap_compute_columns:
+ * @cnc: a #GdaConnection
+ * @attributes: (allow-none): a string describing which LDAP attributes to retreive, or %NULL
+ *
+ * Computes the #GdaColumn of the data model which would be created using @attributes when calling
+ * gda_data_model_ldap_new().
+ *
+ * Returns: (transfer full) (element-type GdaColumn): a list of #GdaColumn objects
+ *
+ * Since: 4.2.8
+ */
+GList *
+gda_data_model_ldap_compute_columns (GdaConnection *cnc, const gchar *attributes)
+{
+ g_return_val_if_fail (GDA_IS_LDAP_CONNECTION (cnc), NULL);
+ typedef GList *(*Func) (GdaConnection*, const gchar *);
+ static Func func = NULL;
+
+ if (!func) {
+ load_ldap_module ();
+ if (!ldap_prov_module)
+ return NULL;
+
+ if (!g_module_symbol (ldap_prov_module, "gdaprov_data_model_ldap_compute_columns", (void **) &func))
+ return NULL;
+ }
+
+ return func (cnc, attributes);
+}
+
+/*
+ * _gda_ldap_describe_entry:
+ * proxy for gda_ldap_describe_entry().
+ */
+GdaLdapEntry *
+_gda_ldap_describe_entry (GdaLdapConnection *cnc, const gchar *dn, GError **error)
+{
+ g_return_val_if_fail (GDA_IS_LDAP_CONNECTION (cnc), NULL);
+ typedef GdaLdapEntry *(*Func) (GdaLdapConnection*, const gchar *, GError **);
+ static Func func = NULL;
+
+ if (!func) {
+ load_ldap_module ();
+ if (!ldap_prov_module)
+ return NULL;
+
+ if (!g_module_symbol (ldap_prov_module, "gdaprov_ldap_describe_entry", (void **) &func))
+ return NULL;
+ }
+
+ return func (cnc, dn, error);
+}
+
+/*
+ * _gda_ldap_get_entry_children:
+ * proxy for gda_ldap_get_entry_children().
+ */
+GdaLdapEntry **
+_gda_ldap_get_entry_children (GdaLdapConnection *cnc, const gchar *dn,
+ gchar **attributes, GError **error)
+{
+ g_return_val_if_fail (GDA_IS_LDAP_CONNECTION (cnc), NULL);
+ typedef GdaLdapEntry **(*Func) (GdaLdapConnection*, const gchar *, gchar **, GError **);
+ static Func func = NULL;
+
+ if (!func) {
+ load_ldap_module ();
+ if (!ldap_prov_module)
+ return NULL;
+
+ if (!g_module_symbol (ldap_prov_module, "gdaprov_ldap_get_entry_children", (void **) &func))
+ return NULL;
+ }
+
+ return func (cnc, dn, attributes, error);
+}
+
+/*
+ * _gda_ldap_dn_split:
+ * proxy for gda_ldap_dn_split().
+ */
+gchar **
+_gda_ldap_dn_split (const gchar *dn, gboolean all)
+{
+ typedef gchar **(*Func) (const gchar *, gboolean);
+ static Func func = NULL;
+
+ if (!func) {
+ load_ldap_module ();
+ if (!ldap_prov_module)
+ return NULL;
+
+ if (!g_module_symbol (ldap_prov_module, "gdaprov_ldap_dn_split", (void **) &func))
+ return NULL;
+ }
+
+ return func (dn, all);
+}
+
+/*
+ * _gda_ldap_is_dn:
+ * proxy for gda_ldap_dn_split().
+ */
+gboolean
+_gda_ldap_is_dn (const gchar *dn)
+{
+ typedef gboolean (*Func) (const gchar *);
+ static Func func = NULL;
+
+ if (!func) {
+ load_ldap_module ();
+ if (!ldap_prov_module)
+ return FALSE;
+
+ if (!g_module_symbol (ldap_prov_module, "gdaprov_ldap_is_dn", (void **) &func))
+ return FALSE;
+ }
+
+ return func (dn);
+}
+
+/*
+ * _gda_ldap_get_base_dn:
+ * proxy for gda_ldap_get_base_dn().
+ */
+const gchar *
+_gda_ldap_get_base_dn (GdaLdapConnection *cnc)
+{
+ g_return_val_if_fail (GDA_IS_LDAP_CONNECTION (cnc), NULL);
+ typedef const gchar *(*Func) (GdaLdapConnection *);
+ static Func func = NULL;
+
+ if (!func) {
+ load_ldap_module ();
+ if (!ldap_prov_module)
+ return NULL;
+
+ if (!g_module_symbol (ldap_prov_module, "gdaprov_ldap_get_base_dn", (void **) &func))
+ return NULL;
+ }
+
+ return func (cnc);
+}
+
+/*
+ * _gda_ldap_get_class_info:
+ * proxy for gda_ldap_get_class_info()
+ */
+GdaLdapClass *
+_gda_ldap_get_class_info (GdaLdapConnection *cnc, const gchar *classname)
+{
+ g_return_val_if_fail (GDA_IS_LDAP_CONNECTION (cnc), NULL);
+ typedef GdaLdapClass *(*Func) (GdaLdapConnection *, const gchar *);
+ static Func func = NULL;
+
+ if (!func) {
+ load_ldap_module ();
+ if (!ldap_prov_module)
+ return NULL;
+
+ if (!g_module_symbol (ldap_prov_module, "gdaprov_ldap_get_class_info", (void **) &func))
+ return NULL;
+ }
+
+ return func (cnc, classname);
+}
+
+/*
+ * _gda_ldap_get_top_classes:
+ * proxy for gda_ldap_get_top_classes()
+ */
+const GSList *
+_gda_ldap_get_top_classes (GdaLdapConnection *cnc)
+{
+ g_return_val_if_fail (GDA_IS_LDAP_CONNECTION (cnc), NULL);
+ typedef const GSList *(*Func) (GdaLdapConnection *);
+ static Func func = NULL;
+
+ if (!func) {
+ load_ldap_module ();
+ if (!ldap_prov_module)
+ return NULL;
+
+ if (!g_module_symbol (ldap_prov_module, "gdaprov_ldap_get_top_classes", (void **) &func))
+ return NULL;
+ }
+
+ return func (cnc);
+}
diff --git a/libgda/gda-data-model-ldap.h b/libgda/gda-data-model-ldap.h
new file mode 100644
index 0000000..0c1e6ac
--- /dev/null
+++ b/libgda/gda-data-model-ldap.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2011 The GNOME Foundation.
+ *
+ * AUTHORS:
+ * Vivien Malerba <malerba gnome-db org>
+ *
+ * This Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GDA_DATA_MODEL_LDAP_H__
+#define __GDA_DATA_MODEL_LDAP_H__
+
+#include <libgda/gda-data-model.h>
+
+G_BEGIN_DECLS
+
+#define GDA_TYPE_DATA_MODEL_LDAP (gda_data_model_ldap_get_type())
+#define GDA_DATA_MODEL_LDAP(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, GDA_TYPE_DATA_MODEL_LDAP, GdaDataModelLdap))
+#define GDA_DATA_MODEL_LDAP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, GDA_TYPE_DATA_MODEL_LDAP, GdaDataModelLdapClass))
+#define GDA_IS_DATA_MODEL_LDAP(obj) (G_TYPE_CHECK_INSTANCE_TYPE(obj, GDA_TYPE_DATA_MODEL_LDAP))
+#define GDA_IS_DATA_MODEL_LDAP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GDA_TYPE_DATA_MODEL_LDAP))
+
+typedef struct _GdaDataModelLdap GdaDataModelLdap;
+typedef struct _GdaDataModelLdapClass GdaDataModelLdapClass;
+typedef struct _GdaDataModelLdapPrivate GdaDataModelLdapPrivate;
+
+struct _GdaDataModelLdap {
+ GObject object;
+ GdaDataModelLdapPrivate *priv;
+};
+
+struct _GdaDataModelLdapClass {
+ GObjectClass parent_class;
+
+ /*< private >*/
+ /* Padding for future expansion */
+ void (*_gda_reserved1) (void);
+ void (*_gda_reserved2) (void);
+ void (*_gda_reserved3) (void);
+ void (*_gda_reserved4) (void);
+};
+
+/**
+ * GdaLdapSearchScope:
+ * @GDA_LDAP_SEARCH_BASE: search of the base object only
+ * @GDA_LDAP_SEARCH_ONELEVEL: search of immediate children of the base object, but does not include the base object itself
+ * @GDA_LDAP_SEARCH_SUBTREE: search of the base object and the entire subtree below the base object
+ *
+ * Defines the search scope of an LDAP search command, relative to the base object.
+ */
+typedef enum {
+ GDA_LDAP_SEARCH_BASE = 1,
+ GDA_LDAP_SEARCH_ONELEVEL = 2,
+ GDA_LDAP_SEARCH_SUBTREE = 3
+} GdaLdapSearchScope;
+
+/**
+ * SECTION:gda-data-model-ldap
+ * @short_description: GdaDataModel to extract LDAP information
+ * @title: GdaDataModelLdap
+ * @stability: Unstable
+ * @see_also: #GdaDataModel
+ *
+ * The #GdaDataModelLdap object allows to perform LDAP searches.
+ *
+ * Note: this type of data model is available only if the LDAP library was found at compilation time and
+ * if the LDAP provider is correctly installed.
+ */
+
+GType gda_data_model_ldap_get_type (void) G_GNUC_CONST;
+GdaDataModel *gda_data_model_ldap_new (GdaConnection *cnc,
+ const gchar *base_dn, const gchar *filter,
+ const gchar *attributes, GdaLdapSearchScope scope);
+
+GList *gda_data_model_ldap_compute_columns (GdaConnection *cnc, const gchar *attributes);
+
+G_END_DECLS
+
+#endif
diff --git a/libgda/gda-data-model.c b/libgda/gda-data-model.c
index 83eda1e..2f2fb17 100644
--- a/libgda/gda-data-model.c
+++ b/libgda/gda-data-model.c
@@ -1007,7 +1007,7 @@ gda_data_model_send_hint (GdaDataModel *model, GdaDataModelHint hint, const GVal
*
* Returns: (transfer none) (element-type GError) (array zero-terminated=1): a pointer to a %NULL terminated array of #GError, or %NULL.
*
- * Since: 5.0
+ * Since: 4.2.6
*/
GError **
gda_data_model_get_exceptions (GdaDataModel *model)
diff --git a/libgda/gda-data-model.h b/libgda/gda-data-model.h
index a7c1b36..0126723 100644
--- a/libgda/gda-data-model.h
+++ b/libgda/gda-data-model.h
@@ -77,7 +77,8 @@ typedef enum {
GDA_DATA_MODEL_FILE_EXIST_ERROR,
GDA_DATA_MODEL_XML_FORMAT_ERROR,
- GDA_DATA_MODEL_TRUNCATED_ERROR
+ GDA_DATA_MODEL_TRUNCATED_ERROR,
+ GDA_DATA_MODEL_OTHER_ERROR
} GdaDataModelError;
/* struct for the interface */
diff --git a/libgda/gda-data-select.c b/libgda/gda-data-select.c
index 72c8c81..5ac9124 100644
--- a/libgda/gda-data-select.c
+++ b/libgda/gda-data-select.c
@@ -3174,7 +3174,7 @@ gda_data_select_get_exceptions (GdaDataModel *model)
*
* Add an exception to @model.
*
- * Since: 5.0
+ * Since: 4.2.6
*/
void
gda_data_select_add_exception (GdaDataSelect *model, GError *error)
diff --git a/libgda/gda-tree-mgr-ldap.c b/libgda/gda-tree-mgr-ldap.c
new file mode 100644
index 0000000..8c45213
--- /dev/null
+++ b/libgda/gda-tree-mgr-ldap.c
@@ -0,0 +1,313 @@
+/*
+ * Copyright (C) 2011 The GNOME Foundation.
+ *
+ * AUTHORS:
+ * Vivien Malerba <malerba gnome-db org>
+ *
+ * This Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <glib/gi18n-lib.h>
+#include <libgda/libgda.h>
+#include "gda-tree-mgr-ldap.h"
+#include "gda-tree-node.h"
+#include <sqlite/virtual/gda-ldap-connection.h>
+
+struct _GdaTreeMgrLdapPriv {
+ GdaLdapConnection *cnc;
+ gchar *dn;
+};
+
+static void gda_tree_mgr_ldap_class_init (GdaTreeMgrLdapClass *klass);
+static void gda_tree_mgr_ldap_init (GdaTreeMgrLdap *tmgr1, GdaTreeMgrLdapClass *klass);
+static void gda_tree_mgr_ldap_dispose (GObject *object);
+static void gda_tree_mgr_ldap_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gda_tree_mgr_ldap_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+/* virtual methods */
+static GSList *gda_tree_mgr_ldap_update_children (GdaTreeManager *manager, GdaTreeNode *node, const GSList *children_nodes,
+ gboolean *out_error, GError **error);
+
+static GObjectClass *parent_class = NULL;
+
+/* properties */
+enum {
+ PROP_0,
+ PROP_CNC,
+ PROP_DN,
+};
+
+/*
+ * GdaTreeMgrLdap class implementation
+ * @klass:
+ */
+static void
+gda_tree_mgr_ldap_class_init (GdaTreeMgrLdapClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ /* virtual methods */
+ ((GdaTreeManagerClass*) klass)->update_children = gda_tree_mgr_ldap_update_children;
+
+ /* Properties */
+ object_class->set_property = gda_tree_mgr_ldap_set_property;
+ object_class->get_property = gda_tree_mgr_ldap_get_property;
+
+ /**
+ * GdaTreeMgrLdap:connection:
+ *
+ * Defines the #GdaLdapConnection to get information from.
+ */
+ g_object_class_install_property (object_class, PROP_CNC,
+ g_param_spec_object ("connection", NULL, "Connection to use",
+ GDA_TYPE_LDAP_CONNECTION,
+ G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
+
+ /**
+ * GdaTreeMgrLdap:dn:
+ *
+ * Defines the Distinguised Name of the LDAP entry to list children from
+ */
+ g_object_class_install_property (object_class, PROP_DN,
+ g_param_spec_string ("dn", NULL, "Distinguised Name",
+ NULL,
+ G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
+ object_class->dispose = gda_tree_mgr_ldap_dispose;
+}
+
+static void
+gda_tree_mgr_ldap_init (GdaTreeMgrLdap *mgr, G_GNUC_UNUSED GdaTreeMgrLdapClass *klass)
+{
+ g_return_if_fail (GDA_IS_TREE_MGR_LDAP (mgr));
+ mgr->priv = g_new0 (GdaTreeMgrLdapPriv, 1);
+}
+
+static void
+gda_tree_mgr_ldap_dispose (GObject *object)
+{
+ GdaTreeMgrLdap *mgr = (GdaTreeMgrLdap *) object;
+
+ g_return_if_fail (GDA_IS_TREE_MGR_LDAP (mgr));
+
+ if (mgr->priv) {
+ if (mgr->priv->cnc)
+ g_object_unref (mgr->priv->cnc);
+ g_free (mgr->priv->dn);
+ g_free (mgr->priv);
+ mgr->priv = NULL;
+ }
+
+ /* chain to parent class */
+ parent_class->dispose (object);
+}
+
+/**
+ * gda_tree_mgr_select_get_type:
+ *
+ * Returns: the GType
+ *
+ * Since: 4.2.8
+ */
+GType
+gda_tree_mgr_ldap_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0)) {
+ static GStaticMutex registering = G_STATIC_MUTEX_INIT;
+ static const GTypeInfo info = {
+ sizeof (GdaTreeMgrLdapClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) gda_tree_mgr_ldap_class_init,
+ NULL,
+ NULL,
+ sizeof (GdaTreeMgrLdap),
+ 0,
+ (GInstanceInitFunc) gda_tree_mgr_ldap_init,
+ 0
+ };
+
+ g_static_mutex_lock (®istering);
+ if (type == 0)
+ type = g_type_register_static (GDA_TYPE_TREE_MANAGER, "GdaTreeMgrLdap", &info, 0);
+ g_static_mutex_unlock (®istering);
+ }
+ return type;
+}
+
+static void
+gda_tree_mgr_ldap_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GdaTreeMgrLdap *mgr;
+
+ mgr = GDA_TREE_MGR_LDAP (object);
+ if (mgr->priv) {
+ switch (param_id) {
+ case PROP_CNC:
+ mgr->priv->cnc = (GdaLdapConnection*) g_value_get_object (value);
+ if (mgr->priv->cnc)
+ g_object_ref (mgr->priv->cnc);
+ break;
+ case PROP_DN:
+ mgr->priv->dn = g_value_dup_string (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+ }
+}
+
+static void
+gda_tree_mgr_ldap_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GdaTreeMgrLdap *mgr;
+
+ mgr = GDA_TREE_MGR_LDAP (object);
+ if (mgr->priv) {
+ switch (param_id) {
+ case PROP_CNC:
+ g_value_set_object (value, mgr->priv->cnc);
+ break;
+ case PROP_DN:
+ g_value_set_string (value, mgr->priv->dn);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+ }
+}
+
+/**
+ * gda_tree_mgr_ldap_new:
+ * @cnc: a #GdaConnection object
+ * @dn: (allow-none): an LDAP Distinguished Name or %NULL
+ *
+ * Creates a new #GdaTreeManager object which will list the children of the LDAP entry which Distinguished name
+ * is @dn. If @dn is %NULL, then the tree manager will look in the tree itself for an attribute named "dn" and
+ * use it.
+ *
+ * Returns: (transfer full): a new #GdaTreeManager object
+ *
+ * Since: 4.2.8
+ */
+GdaTreeManager*
+gda_tree_mgr_ldap_new (GdaConnection *cnc, const gchar *dn)
+{
+ GdaTreeMgrLdap *mgr;
+ g_return_val_if_fail (GDA_IS_LDAP_CONNECTION (cnc), NULL);
+
+ mgr = (GdaTreeMgrLdap*) g_object_new (GDA_TYPE_TREE_MGR_LDAP,
+ "connection", cnc,
+ "dn", dn, NULL);
+ return (GdaTreeManager*) mgr;
+}
+
+static GSList *
+gda_tree_mgr_ldap_update_children (GdaTreeManager *manager, GdaTreeNode *node,
+ G_GNUC_UNUSED const GSList *children_nodes, gboolean *out_error,
+ GError **error)
+{
+ GdaTreeMgrLdap *mgr = GDA_TREE_MGR_LDAP (manager);
+ gchar *real_dn = NULL;
+
+ if (!mgr->priv->cnc) {
+ g_set_error (error, GDA_TREE_MANAGER_ERROR, GDA_TREE_MANAGER_UNKNOWN_ERROR,
+ _("No LDAP connection specified"));
+ if (out_error)
+ *out_error = TRUE;
+ return NULL;
+ }
+
+ if (mgr->priv->dn)
+ real_dn = g_strdup (mgr->priv->dn);
+ else if (node) {
+ /* looking for a dn in @node's attributes */
+ const GValue *cvalue;
+ cvalue = gda_tree_node_fetch_attribute (node, "dn");
+ if (cvalue && (G_VALUE_TYPE (cvalue) == G_TYPE_STRING))
+ real_dn = g_value_dup_string (cvalue);
+ }
+
+ GdaLdapEntry **entries;
+ entries = gda_ldap_get_entry_children (mgr->priv->cnc, real_dn, NULL, error);
+ g_free (real_dn);
+ if (entries) {
+ gint i;
+ GSList *list = NULL;
+ for (i = 0; entries [i]; i++) {
+ GdaTreeNode* snode;
+ GValue *dnv;
+ GdaLdapEntry *lentry;
+ lentry = entries [i];
+ snode = gda_tree_manager_create_node (manager, node, lentry->dn);
+
+ /* full DN */
+ g_value_set_string ((dnv = gda_value_new (G_TYPE_STRING)), lentry->dn);
+ gda_tree_node_set_node_attribute (snode, "dn", dnv, NULL);
+ gda_value_free (dnv);
+
+ /* RDN */
+ gchar **array;
+ array = gda_ldap_dn_split (lentry->dn, FALSE);
+ if (array) {
+ g_value_set_string ((dnv = gda_value_new (G_TYPE_STRING)), array [0]);
+ gda_tree_node_set_node_attribute (snode, "rdn", dnv, NULL);
+ gda_value_free (dnv);
+ g_strfreev (array);
+ }
+
+ if (gda_tree_manager_get_managers (manager)) {
+ g_value_set_boolean ((dnv = gda_value_new (G_TYPE_BOOLEAN)), TRUE);
+ gda_tree_node_set_node_attribute (snode,
+ GDA_ATTRIBUTE_TREE_NODE_UNKNOWN_CHILDREN,
+ dnv, NULL);
+ gda_value_free (dnv);
+ }
+
+ list = g_slist_prepend (list, snode);
+ gda_ldap_entry_free (lentry);
+ }
+ g_free (entries);
+
+ if (node)
+ gda_tree_node_set_node_attribute (node,
+ GDA_ATTRIBUTE_TREE_NODE_UNKNOWN_CHILDREN,
+ NULL, NULL);
+ return list;
+ }
+ else {
+ if (out_error)
+ *out_error = TRUE;
+ return NULL;
+ }
+}
diff --git a/libgda/gda-tree-mgr-ldap.h b/libgda/gda-tree-mgr-ldap.h
new file mode 100644
index 0000000..d079107
--- /dev/null
+++ b/libgda/gda-tree-mgr-ldap.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2011 The GNOME Foundation.
+ *
+ * AUTHORS:
+ * Vivien Malerba <malerba gnome-db org>
+ *
+ * This Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GDA_TREE_MGR_LDAP_H__
+#define __GDA_TREE_MGR_LDAP_H__
+
+#include <libgda/gda-connection.h>
+#include "gda-tree-manager.h"
+
+G_BEGIN_DECLS
+
+#define GDA_TYPE_TREE_MGR_LDAP (gda_tree_mgr_ldap_get_type())
+#define GDA_TREE_MGR_LDAP(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, GDA_TYPE_TREE_MGR_LDAP, GdaTreeMgrLdap))
+#define GDA_TREE_MGR_LDAP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, GDA_TYPE_TREE_MGR_LDAP, GdaTreeMgrLdapClass))
+#define GDA_IS_TREE_MGR_LDAP(obj) (G_TYPE_CHECK_INSTANCE_TYPE(obj, GDA_TYPE_TREE_MGR_LDAP))
+#define GDA_IS_TREE_MGR_LDAP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GDA_TYPE_TREE_MGR_LDAP))
+#define GDA_TREE_MGR_LDAP_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDA_TYPE_TREE_MGR_LDAP, GdaTreeMgrLdapClass))
+
+typedef struct _GdaTreeMgrLdap GdaTreeMgrLdap;
+typedef struct _GdaTreeMgrLdapPriv GdaTreeMgrLdapPriv;
+typedef struct _GdaTreeMgrLdapClass GdaTreeMgrLdapClass;
+
+struct _GdaTreeMgrLdap {
+ GdaTreeManager object;
+ GdaTreeMgrLdapPriv *priv;
+};
+
+struct _GdaTreeMgrLdapClass {
+ GdaTreeManagerClass object_class;
+};
+
+/**
+ * SECTION:gda-tree-mgr-ldap
+ * @short_description: A tree manager which creates a node for each child entry of an LDAP entry
+ * @title: GdaTreeMgrLdap
+ * @stability: Stable
+ * @see_also:
+ *
+ * The #GdaTreeMgrLdap is a #GdaTreeManager object which creates a node for
+ * each child entry of an LDAP entry.
+ *
+ * Note: this type of tree manager is available only if the LDAP library was found at compilation time and
+ * if the LDAP provider is correctly installed.
+ */
+
+GType gda_tree_mgr_ldap_get_type (void) G_GNUC_CONST;
+GdaTreeManager* gda_tree_mgr_ldap_new (GdaConnection *cnc, const gchar *dn);
+
+G_END_DECLS
+
+#endif
diff --git a/libgda/gda-tree-node.c b/libgda/gda-tree-node.c
index 755edec..a4f890e 100644
--- a/libgda/gda-tree-node.c
+++ b/libgda/gda-tree-node.c
@@ -750,13 +750,35 @@ gda_tree_node_set_node_attribute (GdaTreeNode *node, const gchar *attribute, con
{
const GValue *cvalue;
g_return_if_fail (GDA_IS_TREE_NODE (node));
+ g_return_if_fail (attribute);
cvalue = gda_attributes_manager_get (gda_tree_node_attributes_manager, node, attribute);
if ((value && cvalue && !gda_value_differ (cvalue, value)) ||
(!value && !cvalue))
return;
- gda_attributes_manager_set_full (gda_tree_node_attributes_manager, node, attribute, value, destroy);
+ if (!strcmp (attribute, GDA_ATTRIBUTE_TREE_NODE_UNKNOWN_CHILDREN) &&
+ (!value || (G_VALUE_TYPE (value) == G_TYPE_BOOLEAN))) {
+ gboolean ouc = FALSE;
+ gboolean nuc = FALSE;
+ if (cvalue && (G_VALUE_TYPE (cvalue) == G_TYPE_BOOLEAN) &&
+ g_value_get_boolean (cvalue))
+ ouc = TRUE;
+
+ if (value && g_value_get_boolean (value))
+ nuc = TRUE;
+
+ if (ouc != nuc) {
+ gda_attributes_manager_set_full (gda_tree_node_attributes_manager, node,
+ attribute, value, destroy);
+ g_signal_emit (node, gda_tree_node_signals[NODE_HAS_CHILD_TOGGLED], 0, node);
+ g_signal_emit (node, gda_tree_node_signals[NODE_CHANGED], 0, node);
+ return;
+ }
+ }
+
+ gda_attributes_manager_set_full (gda_tree_node_attributes_manager, node, attribute,
+ value, destroy);
g_signal_emit (node, gda_tree_node_signals[NODE_CHANGED], 0, node);
}
diff --git a/libgda/gda-tree.c b/libgda/gda-tree.c
index 31c33ee..cf57bca 100644
--- a/libgda/gda-tree.c
+++ b/libgda/gda-tree.c
@@ -1,5 +1,5 @@
-/* GDA library
- * Copyright (C) 2009 - 2010 The GNOME Foundation.
+/*
+ * Copyright (C) 2009 - 2011 The GNOME Foundation.
*
* AUTHORS:
* Vivien Malerba <malerba gnome-db org>
@@ -32,10 +32,6 @@
struct _GdaTreePrivate {
GSList *managers; /* list of GdaTreeManager */
GdaTreeNode *root;
-
- gboolean update_on_searching; /* set to FALSE if GdaTree's contents is supposed to be constant
- * needs a PROPRERTY because it's now a constant, or even
- * maybe move it to each GdaTreeManager */
};
static void gda_tree_class_init (GdaTreeClass *klass);
@@ -191,8 +187,6 @@ gda_tree_init (GdaTree *tree, G_GNUC_UNUSED GdaTreeClass *klass)
tree->priv->managers = NULL;
take_root_node (tree, gda_tree_node_new (NULL));
-
- tree->priv->update_on_searching = FALSE;
}
static void
@@ -331,7 +325,7 @@ gda_tree_new (void)
/**
* gda_tree_add_manager:
* @tree: a #GdaTree object
- * @manager: a #GdaTreeManager object
+ * @manager: (transfer none): a #GdaTreeManager object
*
* Sets @manager as a top #GdaTreeManager object, which will be responsible for creating top level nodes in @tree.
*
@@ -421,22 +415,71 @@ gboolean
gda_tree_update_part (GdaTree *tree, GdaTreeNode *node, GError **error)
{
GSList *mgrlist;
+ GdaTreeManager *mgr;
+ GdaTreeNode *top;
g_return_val_if_fail (GDA_IS_TREE (tree), FALSE);
g_return_val_if_fail (GDA_IS_TREE_NODE (node), FALSE);
-
- mgrlist = _gda_tree_node_get_managers_for_children (node);
+
+ top = gda_tree_node_get_parent (node);
+ if (!top)
+ top = tree->priv->root;
+ mgr = _gda_tree_node_get_manager_for_child (top, node);
+ mgrlist = (GSList*) gda_tree_manager_get_managers (mgr);
if (mgrlist) {
gboolean res;
res = create_or_update_children (mgrlist, node, FALSE, error);
- g_slist_free (mgrlist);
return res;
}
return TRUE;
}
/**
+ * gda_tree_update_children:
+ * @tree: a #GdaTree object
+ * @node: (allow-none): a #GdaTreeNode node in @tree
+ * @error: (allow-none): a place to store errors, or %NULL
+ *
+ * Update the children of @node in @tree (not recursively, to update recursively, use
+ * gda_tree_update_part()). If @node is %NULL then the top level nodes are updated.
+ *
+ * Returns: TRUE if no error occurred.
+ *
+ * Since: 4.2.8
+ */
+gboolean
+gda_tree_update_children (GdaTree *tree, GdaTreeNode *node, GError **error)
+{
+ GSList *mgrlist;
+ GdaTreeManager *mgr;
+ GdaTreeNode *top;
+
+ g_return_val_if_fail (GDA_IS_TREE (tree), FALSE);
+ g_return_val_if_fail (! node || GDA_IS_TREE_NODE (node), FALSE);
+
+ if (node) {
+ top = gda_tree_node_get_parent (node);
+ if (!top)
+ top = tree->priv->root;
+ mgr = _gda_tree_node_get_manager_for_child (top, node);
+ mgrlist = (GSList*) gda_tree_manager_get_managers (mgr);
+
+ if (mgrlist) {
+ gboolean res;
+ res = create_or_update_children (mgrlist, node, TRUE, error);
+ return res;
+ }
+ }
+ else {
+ /* update top level nodes */
+ create_or_update_children (tree->priv->managers, tree->priv->root, TRUE, error);
+ }
+
+ return TRUE;
+}
+
+/**
* gda_tree_dump:
* @tree: a #GdaTree
* @node: a #GdaTreeNode to start the dump from, or %NULL for a full dump
@@ -523,10 +566,6 @@ gda_tree_get_nodes_in_path (GdaTree *tree, const gchar *tree_path, gboolean use_
static GSList *
real_gda_tree_get_nodes_in_path (GdaTree *tree, GSList *segments, gboolean use_names, GdaTreeNode **out_last_node)
{
- /* update 1st level if necessary */
- if (tree->priv->update_on_searching)
- create_or_update_children (tree->priv->managers, tree->priv->root, TRUE, NULL);
-
if (out_last_node)
*out_last_node = NULL;
@@ -542,7 +581,6 @@ real_gda_tree_get_nodes_in_path (GdaTree *tree, GSList *segments, gboolean use_n
GSList *seglist;
GdaTreeNode *node;
GdaTreeNode *parent;
- GSList *mgrlist;
for (seglist = segments, parent = tree->priv->root;
seglist;
seglist = seglist->next, parent = node) {
@@ -550,32 +588,10 @@ real_gda_tree_get_nodes_in_path (GdaTree *tree, GSList *segments, gboolean use_n
node = gda_tree_node_get_child_name (parent, (gchar *) seglist->data);
else
node = gda_tree_node_get_child_index (parent, atoi ((gchar *) seglist->data)); /* Flawfinder: ignore */
- if (!node && tree->priv->update_on_searching) {
- /* update level if necessary */
- mgrlist = _gda_tree_node_get_managers_for_children (parent);
-
- if (mgrlist) {
- create_or_update_children (mgrlist, parent, TRUE, NULL);
- g_slist_free (mgrlist);
- }
-
- /* try again now */
- if (use_names)
- node = gda_tree_node_get_child_name (parent, (gchar *) seglist->data);
- else
- node = gda_tree_node_get_child_index (parent, atoi ((gchar *) seglist->data)); /* Flawfinder: ignore */
- }
if (!node)
return NULL;
}
- if (tree->priv->update_on_searching) {
- mgrlist = _gda_tree_node_get_managers_for_children (node);
- if (mgrlist) {
- create_or_update_children (mgrlist, node, TRUE, NULL);
- g_slist_free (mgrlist);
- }
- }
if (out_last_node) {
*out_last_node = node;
return NULL;
diff --git a/libgda/gda-tree.h b/libgda/gda-tree.h
index fa792db..b49f023 100644
--- a/libgda/gda-tree.h
+++ b/libgda/gda-tree.h
@@ -88,6 +88,7 @@ void gda_tree_add_manager (GdaTree *tree, GdaTreeManager *manage
void gda_tree_clean (GdaTree *tree);
gboolean gda_tree_update_all (GdaTree *tree, GError **error);
gboolean gda_tree_update_part (GdaTree *tree, GdaTreeNode *node, GError **error);
+gboolean gda_tree_update_children (GdaTree *tree, GdaTreeNode *node, GError **error);
GSList *gda_tree_get_nodes_in_path (GdaTree *tree, const gchar *tree_path, gboolean use_names);
GdaTreeNode *gda_tree_get_node (GdaTree *tree, const gchar *tree_path, gboolean use_names);
diff --git a/libgda/libgda.h.in b/libgda/libgda.h.in
index c90f9b0..a65bc03 100644
--- a/libgda/libgda.h.in
+++ b/libgda/libgda.h.in
@@ -35,6 +35,8 @@
#include <libgda/gda-data-comparator.h>
#include <libgda/gda-data-model-array.h>
@LIBGDA_BDB_INC@
+ LIBGDA_LDAP_INC@
+ LIBGDA_LDAP_INC2@
#include <libgda/gda-data-model.h>
#include <libgda/gda-data-model-iter.h>
#include <libgda/gda-data-model-import.h>
diff --git a/libgda/libgda.symbols b/libgda/libgda.symbols
index 886fea1..0cf408a 100644
--- a/libgda/libgda.symbols
+++ b/libgda/libgda.symbols
@@ -163,7 +163,6 @@
gda_connection_repetitive_statement_execute
gda_connection_rollback_savepoint
gda_connection_rollback_transaction
- gda_connection_schema_get_type
gda_connection_statement_execute
gda_connection_statement_execute_non_select
gda_connection_statement_execute_select
@@ -268,6 +267,11 @@
gda_data_model_iter_move_to_row
gda_data_model_iter_move_to_row_default
gda_data_model_iter_set_value_at
+#ifdef HAVE_LDAP
+ gda_data_model_ldap_compute_columns
+ gda_data_model_ldap_get_type
+ gda_data_model_ldap_new
+#endif
gda_data_model_remove_row
gda_data_model_reset
gda_data_model_row_inserted
@@ -395,6 +399,20 @@
gda_identifier_hash
gda_init
gda_lang_locale
+#ifdef HAVE_LDAP
+ gda_ldap_connection_get_type
+ gda_ldap_get_class_info
+ gda_ldap_get_top_classes
+ gda_ldap_connection_declare_table
+ gda_ldap_connection_describe_table
+ gda_ldap_connection_get_base_dn
+ gda_ldap_connection_undeclare_table
+ gda_ldap_describe_entry
+ gda_ldap_dn_split
+ gda_ldap_entry_free
+ gda_ldap_get_entry_children
+ gda_ldap_is_dn
+#endif
gda_locale_changed
gda_lockable_get_type
gda_lockable_lock
@@ -838,6 +856,10 @@
gda_tree_mgr_columns_new
gda_tree_mgr_label_get_type
gda_tree_mgr_label_new
+#ifdef HAVE_LDAP
+ gda_tree_mgr_ldap_get_type
+ gda_tree_mgr_ldap_new
+#endif
gda_tree_mgr_schemas_get_type
gda_tree_mgr_schemas_new
gda_tree_mgr_select_get_type
@@ -859,6 +881,7 @@
gda_tree_node_set_node_attribute
gda_tree_set_attribute
gda_tree_update_all
+ gda_tree_update_children
gda_tree_update_part
gda_ushort_get_type
gda_utility_check_data_model
@@ -874,7 +897,6 @@
gda_value_get_binary
gda_value_get_blob
gda_value_get_geometric_point
- gda_value_get_list
gda_value_get_numeric
gda_value_get_short
gda_value_get_time
@@ -895,7 +917,6 @@
gda_value_set_from_string
gda_value_set_from_value
gda_value_set_geometric_point
- gda_value_set_list
gda_value_set_null
gda_value_set_numeric
gda_value_set_short
diff --git a/libgda/sqlite/gda-sqlite-provider.c b/libgda/sqlite/gda-sqlite-provider.c
index b766e82..9bfe52a 100644
--- a/libgda/sqlite/gda-sqlite-provider.c
+++ b/libgda/sqlite/gda-sqlite-provider.c
@@ -3061,13 +3061,26 @@ gda_sqlite_provider_statement_execute (GdaServerProvider *provider, GdaConnectio
flags = GDA_DATA_MODEL_ACCESS_CURSOR_FORWARD;
data_model = (GObject *) _gda_sqlite_recordset_new (cnc, ps, params, flags, col_types, empty_rs);
- gda_connection_internal_statement_executed (cnc, stmt, params, NULL);
- if (new_ps)
- g_object_unref (ps);
- if (allow_noparam)
- g_object_set (data_model, "auto-reset", TRUE, NULL);
- pending_blobs_free_list (blobs_list);
- return data_model;
+ GError **exceptions;
+ exceptions = gda_data_model_get_exceptions (GDA_DATA_MODEL (data_model));
+ if (exceptions && exceptions[0]) {
+ GError *e;
+ e = g_error_copy (exceptions[0]);
+ event = gda_connection_point_available_event (cnc, GDA_CONNECTION_EVENT_ERROR);
+ gda_connection_event_set_description (event, e->message ? e->message : _("No detail"));
+ g_propagate_error (error, e);
+ g_object_unref (data_model);
+ return NULL;
+ }
+ else {
+ gda_connection_internal_statement_executed (cnc, stmt, params, NULL);
+ if (new_ps)
+ g_object_unref (ps);
+ if (allow_noparam)
+ g_object_set (data_model, "auto-reset", TRUE, NULL);
+ pending_blobs_free_list (blobs_list);
+ return data_model;
+ }
}
else {
int status, changes;
diff --git a/libgda/sqlite/gda-sqlite-recordset.c b/libgda/sqlite/gda-sqlite-recordset.c
index ff7c890..60bcab7 100644
--- a/libgda/sqlite/gda-sqlite-recordset.c
+++ b/libgda/sqlite/gda-sqlite-recordset.c
@@ -558,10 +558,6 @@ fetch_next_sqlite_row (GdaSqliteRecordset *model, gboolean do_store, GError **er
case SQLITE_BUSY:
/* nothing to do */
break;
- case SQLITE_ERROR:
- g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
- GDA_SERVER_PROVIDER_INTERNAL_ERROR, "%s", SQLITE3_CALL (sqlite3_errmsg) (cdata->connection));
- break;
case SQLITE_DONE:
GDA_DATA_SELECT (model)->advertized_nrows = model->priv->next_row_num;
SQLITE3_CALL (sqlite3_reset) (ps->sqlite_stmt);
@@ -572,8 +568,10 @@ fetch_next_sqlite_row (GdaSqliteRecordset *model, gboolean do_store, GError **er
GDA_SERVER_PROVIDER_INTERNAL_ERROR,
"%s", _("SQLite provider fatal internal error"));
break;
+ case SQLITE_ERROR:
default: {
GError *lerror = NULL;
+ SQLITE3_CALL (sqlite3_reset) (ps->sqlite_stmt);
if (rc == SQLITE_IOERR_TRUNCATE)
g_set_error (&lerror, GDA_DATA_MODEL_ERROR,
GDA_DATA_MODEL_TRUNCATED_ERROR, _("Tuncated data"));
@@ -582,9 +580,9 @@ fetch_next_sqlite_row (GdaSqliteRecordset *model, gboolean do_store, GError **er
GDA_SERVER_PROVIDER_INTERNAL_ERROR,
"%s", SQLITE3_CALL (sqlite3_errmsg) (cdata->connection));
gda_data_select_add_exception (GDA_DATA_SELECT (model), lerror);
-
+ if (rc == SQLITE_ERROR)
+ g_propagate_error (error, g_error_copy (lerror));
GDA_DATA_SELECT (model)->advertized_nrows = model->priv->next_row_num;
- SQLITE3_CALL (sqlite3_reset) (ps->sqlite_stmt);
break;
}
}
diff --git a/libgda/sqlite/virtual/.gitignore b/libgda/sqlite/virtual/.gitignore
new file mode 100644
index 0000000..62d0d62
--- /dev/null
+++ b/libgda/sqlite/virtual/.gitignore
@@ -0,0 +1 @@
+libgda-virtual.h
diff --git a/libgda/sqlite/virtual/Makefile.am b/libgda/sqlite/virtual/Makefile.am
index 225e5e4..5ded4e2 100644
--- a/libgda/sqlite/virtual/Makefile.am
+++ b/libgda/sqlite/virtual/Makefile.am
@@ -9,6 +9,13 @@ endif
noinst_LTLIBRARIES = libgda-virtual-5.0.la
+if LDAP
+GDA_LDAP_H=gda-ldap-connection.h
+GDA_LDAP_S=gda-ldap-connection.c
+DEF_FLAGS=-DHAVE_LDAP
+endif
+
+
AM_CPPFLAGS = \
-I$(top_srcdir) \
-I$(top_builddir) \
@@ -27,6 +34,7 @@ virtual_headers = \
gda-vprovider-hub.h \
gda-virtual-connection.h \
gda-virtual-provider.h \
+ $(GDA_LDAP_H) \
libgda-virtual.h
libgda_virtual_5_0_la_SOURCES = \
@@ -37,7 +45,8 @@ libgda_virtual_5_0_la_SOURCES = \
gda-vprovider-data-model.c \
gda-vprovider-hub.c \
gda-virtual-connection.c \
- gda-virtual-provider.c
+ $(GDA_LDAP_S) \
+ gda-virtual-provider.c
gdaincludedir=$(includedir)/libgda-$(GDA_ABI_MAJOR_VERSION).$(GDA_ABI_MINOR_VERSION)/libgda/virtual
gdainclude_HEADERS=$(virtual_headers)
diff --git a/libgda/sqlite/virtual/gda-ldap-connection.c b/libgda/sqlite/virtual/gda-ldap-connection.c
new file mode 100644
index 0000000..ea9ccdb
--- /dev/null
+++ b/libgda/sqlite/virtual/gda-ldap-connection.c
@@ -0,0 +1,1025 @@
+/*
+ * Copyright (C) 2011 The GNOME Foundation.
+ *
+ * AUTHORS:
+ * Vivien Malerba <malerba gnome-db org>
+ *
+ * This Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <glib/gi18n-lib.h>
+#include <string.h>
+#include <gda-util.h>
+#include "gda-ldap-connection.h"
+#include <libgda/gda-connection-private.h>
+#include <sql-parser/gda-sql-parser.h>
+
+/* "inherits" GdaVconnectionDataModelSpec */
+typedef struct {
+ GdaVconnectionDataModelSpec spec;
+ GdaConnection *ldap_cnc;
+ gchar *table_name;
+ gchar *base_dn;
+ gchar *filter;
+ gchar *attributes;
+ GList *columns;
+ GdaLdapSearchScope scope;
+
+ GHashTable *filters_hash; /* key = string; value = a ComputedFilter pointer */
+} LdapTableMap;
+
+static void
+_ldap_table_map_free (LdapTableMap *map)
+{
+ if (map->ldap_cnc)
+ g_object_unref (map->ldap_cnc);
+ g_free (map->table_name);
+ g_free (map->base_dn);
+ g_free (map->filter);
+ g_free (map->attributes);
+ if (map->columns) {
+ g_list_foreach (map->columns, (GFunc) g_object_unref, NULL);
+ g_list_free (map->columns);
+ }
+ if (map->filters_hash)
+ g_hash_table_destroy (map->filters_hash);
+ g_free (map);
+}
+
+struct _GdaLdapConnectionPrivate {
+ GSList *maps; /* list of #LdapTableMap, no ref held there */
+ gchar *startup_file;
+ gboolean loading_startup_file;
+};
+
+static void gda_ldap_connection_class_init (GdaLdapConnectionClass *klass);
+static void gda_ldap_connection_init (GdaLdapConnection *cnc, GdaLdapConnectionClass *klass);
+static void gda_ldap_connection_dispose (GObject *object);
+static void gda_ldap_connection_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gda_ldap_connection_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static GObjectClass *parent_class = NULL;
+
+/* properties */
+enum
+{
+ PROP_0,
+ PROP_STARTUP_FILE
+};
+
+static void
+update_connection_startup_file (GdaLdapConnection *cnc)
+{
+ if (! cnc->priv->startup_file || cnc->priv->loading_startup_file)
+ return;
+
+ GSList *list;
+ GString *string = NULL;
+ GError *lerror = NULL;
+
+ string = g_string_new ("");
+ for (list = cnc->priv->maps; list; list = list->next) {
+ LdapTableMap *map = (LdapTableMap*) list->data;
+ g_string_append_printf (string, "CREATE LDAP TABLE %s ", map->table_name);
+ if (map->base_dn)
+ g_string_append_printf (string, "BASE='%s' ", map->base_dn);
+ if (map->filter)
+ g_string_append_printf (string, "FILTER='%s' ", map->filter);
+ if (map->attributes)
+ g_string_append_printf (string, "ATTRIBUTES='%s' ", map->attributes);
+ g_string_append (string, "SCOPE=");
+ switch (map->scope) {
+ case GDA_LDAP_SEARCH_BASE:
+ g_string_append (string, "'BASE';\n");
+ break;
+ case GDA_LDAP_SEARCH_ONELEVEL:
+ g_string_append (string, "'ONELEVEL';\n");
+ break;
+ case GDA_LDAP_SEARCH_SUBTREE:
+ g_string_append (string, "'SUBTREE';\n");
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+ }
+ if (! g_file_set_contents (cnc->priv->startup_file, string->str, -1, &lerror)) {
+ GdaConnectionEvent *event;
+ gchar *msg;
+ event = gda_connection_point_available_event (GDA_CONNECTION (cnc),
+ GDA_CONNECTION_EVENT_WARNING);
+ msg = g_strdup_printf (_("Error storing list of created LDAP tables: %s"),
+ lerror && lerror->message ? lerror->message : _("No detail"));
+ gda_connection_event_set_description (event, msg);
+ gda_connection_add_event (GDA_CONNECTION (cnc), event);
+ g_free (msg);
+ g_clear_error (&lerror);
+ }
+}
+
+#ifdef GDA_DEBUG_NO
+static void
+dump_vtables (GdaLdapConnection *cnc)
+{
+ GSList *list;
+ g_print ("LDAP tables: %d\n", g_slist_length (cnc->priv->maps));
+ for (list = cnc->priv->maps; list; list = list->next) {
+ LdapTableMap *map = (LdapTableMap*) list->data;
+ g_print (" LDAP Vtable: %s (map %p)\n", map->table_name, map);
+ }
+}
+#endif
+
+static void
+vtable_created (GdaVconnectionDataModel *cnc, const gchar *table_name)
+{
+#ifdef GDA_DEBUG_NO
+ g_print ("VTable created: %s\n", table_name);
+ dump_vtables (GDA_LDAP_CONNECTION (cnc));
+#endif
+ if (GDA_VCONNECTION_DATA_MODEL_CLASS (parent_class)->vtable_created)
+ GDA_VCONNECTION_DATA_MODEL_CLASS (parent_class)->vtable_created (cnc, table_name);
+ update_connection_startup_file (GDA_LDAP_CONNECTION (cnc));
+}
+
+static void
+vtable_dropped (GdaVconnectionDataModel *cnc, const gchar *table_name)
+{
+ GdaLdapConnection *lcnc;
+ LdapTableMap *map = NULL;
+ GSList *list;
+
+ lcnc = GDA_LDAP_CONNECTION (cnc);
+ for (list = lcnc->priv->maps; list; list = list->next) {
+ if (!strcmp (((LdapTableMap*)list->data)->table_name, table_name)) {
+ map = (LdapTableMap*)list->data;
+ break;
+ }
+ }
+ if (map) {
+ lcnc->priv->maps = g_slist_remove (lcnc->priv->maps, map);
+#ifdef GDA_DEBUG_NO
+ g_print ("VTable dropped: %s\n", table_name);
+ dump_vtables (lcnc);
+#endif
+ }
+ if (GDA_VCONNECTION_DATA_MODEL_CLASS (parent_class)->vtable_dropped)
+ GDA_VCONNECTION_DATA_MODEL_CLASS (parent_class)->vtable_dropped (cnc, table_name);
+ update_connection_startup_file (GDA_LDAP_CONNECTION (cnc));
+}
+
+/*
+ * GdaLdapConnection class implementation
+ */
+static void
+gda_ldap_connection_class_init (GdaLdapConnectionClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ parent_class = g_type_class_peek_parent (klass);
+ object_class->dispose = gda_ldap_connection_dispose;
+ GDA_VCONNECTION_DATA_MODEL_CLASS (klass)->vtable_created = vtable_created;
+ GDA_VCONNECTION_DATA_MODEL_CLASS (klass)->vtable_dropped = vtable_dropped;
+
+ /* Properties */
+ object_class->set_property = gda_ldap_connection_set_property;
+ object_class->get_property = gda_ldap_connection_get_property;
+ g_object_class_install_property (object_class, PROP_STARTUP_FILE,
+ g_param_spec_string ("startup-file", NULL, _("File used to store startup data"), NULL,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+}
+
+static void
+dsn_set_cb (GdaLdapConnection *cnc, G_GNUC_UNUSED GParamSpec *pspec, G_GNUC_UNUSED gpointer data)
+{
+ gchar *fname, *tmp, *dsn;
+ g_object_get (cnc, "dsn", &dsn, NULL);
+ tmp = g_strdup_printf ("ldap-%s.start", dsn);
+ g_free (dsn);
+ fname = g_build_path (G_DIR_SEPARATOR_S, g_get_user_data_dir (),
+ "libgda", tmp, NULL);
+ g_free (tmp);
+ g_free (cnc->priv->startup_file);
+ cnc->priv->startup_file = fname;
+}
+
+static void
+conn_opened_cb (GdaLdapConnection *cnc, G_GNUC_UNUSED gpointer data)
+{
+ if (!cnc->priv->startup_file)
+ return;
+
+ cnc->priv->loading_startup_file = TRUE;
+
+ GdaSqlParser *parser;
+ GdaBatch *batch;
+ GError *lerror = NULL;
+ parser = gda_connection_create_parser (GDA_CONNECTION (cnc));
+ if (!parser)
+ parser = gda_sql_parser_new ();
+ batch = gda_sql_parser_parse_file_as_batch (parser, cnc->priv->startup_file, &lerror);
+ if (batch) {
+ GSList *list;
+ list = gda_connection_batch_execute (GDA_CONNECTION (cnc), batch, NULL, 0, &lerror);
+ g_slist_foreach (list, (GFunc) g_object_unref, NULL);
+ g_slist_free (list);
+ g_object_unref (batch);
+ }
+ if (lerror) {
+ GdaConnectionEvent *event;
+ gchar *msg;
+ event = gda_connection_point_available_event (GDA_CONNECTION (cnc),
+ GDA_CONNECTION_EVENT_WARNING);
+ msg = g_strdup_printf (_("Error recreating LDAP tables: %s"),
+ lerror && lerror->message ? lerror->message : _("No detail"));
+ gda_connection_event_set_description (event, msg);
+ gda_connection_add_event (GDA_CONNECTION (cnc), event);
+ g_free (msg);
+ g_clear_error (&lerror);
+ }
+ g_object_unref (parser);
+
+ cnc->priv->loading_startup_file = FALSE;
+}
+
+static void
+gda_ldap_connection_init (GdaLdapConnection *cnc, G_GNUC_UNUSED GdaLdapConnectionClass *klass)
+{
+ cnc->priv = g_new (GdaLdapConnectionPrivate, 1);
+ cnc->priv->maps = NULL;
+ cnc->priv->startup_file = NULL;
+ cnc->priv->loading_startup_file = FALSE;
+
+ g_signal_connect (cnc, "notify::dsn",
+ G_CALLBACK (dsn_set_cb), NULL);
+ g_signal_connect (cnc, "conn-opened",
+ G_CALLBACK (conn_opened_cb), NULL);
+}
+
+static void
+gda_ldap_connection_dispose (GObject *object)
+{
+ GdaLdapConnection *cnc = (GdaLdapConnection *) object;
+
+ g_return_if_fail (GDA_IS_LDAP_CONNECTION (cnc));
+
+ /* free memory */
+ if (cnc->priv) {
+ if (cnc->priv->maps)
+ g_slist_free (cnc->priv->maps);
+ g_free (cnc->priv->startup_file);
+ g_free (cnc->priv);
+ cnc->priv = NULL;
+ }
+
+ /* chain to parent class */
+ parent_class->dispose (object);
+}
+
+GType
+gda_ldap_connection_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0)) {
+ static GStaticMutex registering = G_STATIC_MUTEX_INIT;
+ if (type == 0) {
+ static GTypeInfo info = {
+ sizeof (GdaLdapConnectionClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) gda_ldap_connection_class_init,
+ NULL, NULL,
+ sizeof (GdaLdapConnection),
+ 0,
+ (GInstanceInitFunc) gda_ldap_connection_init,
+ 0
+ };
+
+ g_static_mutex_lock (®istering);
+ if (type == 0)
+ type = g_type_register_static (GDA_TYPE_VCONNECTION_DATA_MODEL, "GdaLdapConnection", &info, 0);
+ g_static_mutex_unlock (®istering);
+ }
+ }
+
+ return type;
+}
+
+static void
+gda_ldap_connection_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GdaLdapConnection *cnc;
+ cnc = GDA_LDAP_CONNECTION (object);
+ if (cnc->priv) {
+ switch (param_id) {
+ case PROP_STARTUP_FILE: {
+ if (cnc->priv->startup_file) {
+ /* don't override any preexisting setting from a DSN */
+ gchar *dsn;
+ g_object_get (cnc, "dsn", &dsn, NULL);
+ if (dsn)
+ g_free (dsn);
+ else {
+ g_free (cnc->priv->startup_file);
+ cnc->priv->startup_file = NULL;
+ }
+ }
+ if (! cnc->priv->startup_file) {
+ if (g_value_get_string (value))
+ cnc->priv->startup_file = g_value_dup_string (value);
+ }
+ break;
+ }
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+ }
+}
+
+static void
+gda_ldap_connection_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GdaLdapConnection *cnc;
+ cnc = GDA_LDAP_CONNECTION (object);
+ if (cnc->priv) {
+ switch (param_id) {
+ case PROP_STARTUP_FILE:
+ g_value_set_string (value, cnc->priv->startup_file);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+ }
+}
+
+static GList *
+_ldap_create_columns_func (GdaVconnectionDataModelSpec *spec, G_GNUC_UNUSED GError **error)
+{
+ LdapTableMap *map = (LdapTableMap *) spec;
+ if (!map->columns)
+ map->columns = gda_data_model_ldap_compute_columns (map->ldap_cnc, map->attributes);
+ g_list_foreach (map->columns, (GFunc) g_object_ref, NULL);
+ return g_list_copy (map->columns);
+}
+
+static gchar *
+make_string_for_filter (GdaVconnectionDataModelFilter *info)
+{
+ GString *string;
+ gint i;
+
+ string = g_string_new ("");
+ for (i = 0; i < info->nConstraint; i++) {
+ const struct GdaVirtualConstraint *cons;
+ cons = &(info->aConstraint [i]);
+ g_string_append_printf (string, "|%d,%d", cons->iColumn, cons->op);
+ }
+ return g_string_free (string, FALSE);
+}
+
+typedef struct {
+ gint dn_constindex; /* constraint number to set the DN from when actually executing the LDAP search, or -1 */
+ gchar *ldap_filter; /* in LDAP format, with @xxx@ to bind values */
+ struct GdaVirtualConstraintUsage *out_const;
+} ComputedFilter;
+
+static void
+computed_filter_free (ComputedFilter *filter)
+{
+ g_free (filter->out_const);
+ g_free (filter->ldap_filter);
+ g_free (filter);
+}
+
+#define MARKER_ESCAPE_CHAR 1
+#define MARKER_GLOB_CHAR 2
+static void
+_ldap_table_create_filter (GdaVconnectionDataModelSpec *spec, GdaVconnectionDataModelFilter *info)
+{
+ /*
+ * REM:
+ * - LDAP does not allow filtering on the DN => filter only is some cases
+ * - LDAP does not handle ordering of results => the 'ORDER BY' constraint is ignored
+ * - the '>' and '<' operators are not allowed in search strings => using '>=' and '<=' and
+ * have SQLite do the actual check
+ */
+ LdapTableMap *map = (LdapTableMap *) spec;
+ GString *filter_string = NULL;
+ gint i, ncols;
+ gint dn_constindex = -1;
+ gchar *hash;
+
+ info->orderByConsumed = FALSE;
+ hash = make_string_for_filter (info);
+ if (map->filters_hash) {
+ ComputedFilter *filter;
+ filter = g_hash_table_lookup (map->filters_hash, hash);
+ if (filter) {
+ info->idxPointer = (gpointer) filter;
+ info->orderByConsumed = FALSE;
+ memcpy (info->aConstraintUsage,
+ filter->out_const,
+ sizeof (struct GdaVirtualConstraintUsage) * info->nConstraint);
+ /*g_print ("Reusing filter %p, hash=[%s]\n", filter, hash);*/
+ g_free (hash);
+ return;
+ }
+ }
+
+ if (!map->columns)
+ map->columns = gda_data_model_ldap_compute_columns (map->ldap_cnc, map->attributes);
+
+ ncols = g_list_length (map->columns);
+ for (i = 0; i < info->nConstraint; i++) {
+ const struct GdaVirtualConstraint *cons;
+ cons = &(info->aConstraint [i]);
+ const gchar *attrname;
+
+ info->aConstraintUsage[i].argvIndex = i+1;
+ info->aConstraintUsage[i].omit = TRUE;
+
+ if (cons->iColumn < 0) {
+ g_warning ("Internal error: negative column number!");
+ goto nofilter;
+ }
+ if (cons->iColumn >= ncols) {
+ g_warning ("Internal error: SQLite's virtual table column %d is not known for "
+ "table '%s', which has %d column(s)", cons->iColumn, map->table_name,
+ ncols);
+ goto nofilter;
+ }
+ if (cons->iColumn == 0) {
+ /* try to optimize on the DN column */
+ if ((map->scope == GDA_LDAP_SEARCH_BASE) ||
+ (map->scope == GDA_LDAP_SEARCH_ONELEVEL))
+ goto nofilter;
+ if (cons->op != GDA_SQL_OPERATOR_TYPE_EQ)
+ goto nofilter;
+ if (dn_constindex != -1) /* DN is already filtered by a constraint */
+ goto nofilter;
+ dn_constindex = i;
+ continue;
+ }
+
+ attrname = gda_column_get_name (GDA_COLUMN (g_list_nth_data (map->columns, cons->iColumn)));
+ if (! filter_string) {
+ if ((info->nConstraint > 1) || map->filter)
+ filter_string = g_string_new ("(& ");
+ else
+ filter_string = g_string_new ("");
+ if (map->filter)
+ g_string_append (filter_string, map->filter);
+ }
+ switch (cons->op) {
+ case GDA_SQL_OPERATOR_TYPE_EQ:
+ g_string_append_printf (filter_string, "(%s=%c)", attrname, MARKER_ESCAPE_CHAR);
+ break;
+ case GDA_SQL_OPERATOR_TYPE_GT:
+ g_string_append_printf (filter_string, "(%s>=%c)", attrname, MARKER_ESCAPE_CHAR);
+ info->aConstraintUsage[i].omit = FALSE;
+ break;
+ case GDA_SQL_OPERATOR_TYPE_LEQ:
+ g_string_append_printf (filter_string, "(%s<=%c)", attrname, MARKER_ESCAPE_CHAR);
+ info->aConstraintUsage[i].omit = FALSE;
+ break;
+ case GDA_SQL_OPERATOR_TYPE_LT:
+ g_string_append_printf (filter_string, "(%s<=%c)", attrname, MARKER_ESCAPE_CHAR);
+ break;
+ case GDA_SQL_OPERATOR_TYPE_GEQ:
+ g_string_append_printf (filter_string, "(%s>=%c)", attrname, MARKER_ESCAPE_CHAR);
+ break;
+ case GDA_SQL_OPERATOR_TYPE_REGEXP:
+ g_string_append_printf (filter_string, "(%s=%c)", attrname, MARKER_GLOB_CHAR);
+ break;
+ default:
+ /* Can't be done with LDAP */
+ goto nofilter;
+ }
+ }
+
+ if (!filter_string && (dn_constindex == -1))
+ goto nofilter;
+
+ if (filter_string && ((info->nConstraint > 1) || map->filter))
+ g_string_append_c (filter_string, ')');
+ /*g_print ("FILTER: [%s]\n", filter_string->str);*/
+
+ ComputedFilter *filter;
+ filter = g_new0 (ComputedFilter, 1);
+ filter->dn_constindex = dn_constindex;
+ if (filter_string)
+ filter->ldap_filter = g_string_free (filter_string, FALSE);
+ else if (map->filter)
+ filter->ldap_filter = g_strdup (map->filter);
+ filter->out_const = g_new (struct GdaVirtualConstraintUsage, info->nConstraint);
+ memcpy (filter->out_const,
+ info->aConstraintUsage,
+ sizeof (struct GdaVirtualConstraintUsage) * info->nConstraint);
+
+ info->idxPointer = (gpointer) filter;
+ if (! map->filters_hash)
+ map->filters_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free,
+ (GDestroyNotify) computed_filter_free);
+ g_hash_table_insert (map->filters_hash, hash, filter);
+ /*g_print ("There are now %d statements in store...\n", g_hash_table_size (map->filters_hash));*/
+ return;
+
+ nofilter:
+ if (filter_string)
+ g_string_free (filter_string, TRUE);
+ for (i = 0; i < info->nConstraint; i++) {
+ info->aConstraintUsage[i].argvIndex = 0;
+ info->aConstraintUsage[i].omit = TRUE;
+ }
+ info->idxPointer = NULL;
+}
+
+static GdaDataModel *
+_ldap_table_create_model_func (GdaVconnectionDataModelSpec *spec, G_GNUC_UNUSED int idxNum, const char *idxStr,
+ int argc, GValue **argv)
+{
+ LdapTableMap *map = (LdapTableMap *) spec;
+ GdaDataModel *model;
+
+ if (idxStr) {
+ const gchar *ptr;
+ gint pos;
+ GString *real_filter = NULL;
+ ComputedFilter *filter = (ComputedFilter *) idxStr;
+
+ for (pos = 0, ptr = filter->ldap_filter ? filter->ldap_filter : ""; *ptr; ptr++) {
+ if (pos == filter->dn_constindex)
+ pos++; /* skip this constraint position */
+ if (! real_filter)
+ real_filter = g_string_new ("");
+ if ((*ptr == MARKER_ESCAPE_CHAR) || (*ptr == MARKER_GLOB_CHAR)){
+ gchar *str, *sptr;
+ gchar marker;
+ marker = *ptr;
+ g_assert (pos < argc);
+ str = gda_value_stringify (argv[pos]);
+ for (sptr = str; *sptr; sptr++) {
+ if ((*sptr == ')') || (*sptr == '(') || (*sptr == '\\') || (*sptr == '*'))
+ break;
+ if ((marker == MARKER_GLOB_CHAR) && (*sptr == '%'))
+ break;
+ }
+ if (*sptr) {
+ /* make the substitutions */
+ GString *string;
+ string = g_string_new ("");
+ for (sptr = str; *sptr; sptr++) {
+ if (*sptr == ')')
+ g_string_append (string, "\\29");
+ else if (*sptr == '(')
+ g_string_append (string, "\\28");
+ else if (*sptr == '\\')
+ g_string_append (string, "\\5c");
+ else if (*sptr == '*')
+ g_string_append (string, "\\2a");
+ else if ((marker == MARKER_GLOB_CHAR) && (*sptr == '%'))
+ g_string_append_c (string, '*');
+ else
+ g_string_append_c (string, *sptr);
+ }
+ g_free (str);
+ str = g_string_free (string, FALSE);
+ }
+ g_string_append (real_filter, str);
+ g_free (str);
+ pos++;
+ }
+ else
+ g_string_append_c (real_filter, *ptr);
+ }
+
+ gchar *real_dn = NULL;
+ GdaLdapSearchScope real_scope = map->scope;
+ if (filter->dn_constindex != -1) {
+ /* check that the DN is a child of the data model's base DN */
+ const gchar *bdn;
+ gchar *tmp;
+ bdn = map->base_dn;
+ if (!bdn)
+ bdn = gda_ldap_connection_get_base_dn (GDA_LDAP_CONNECTION (map->ldap_cnc));
+ g_assert (bdn);
+ tmp = gda_value_stringify (argv[filter->dn_constindex]);
+ if (g_str_has_suffix (tmp, bdn)) {
+ real_scope = GDA_LDAP_SEARCH_BASE;
+ real_dn = gda_value_stringify (argv[filter->dn_constindex]);
+ }
+ else {
+ /* return empty set */
+ if (real_filter)
+ g_string_free (real_filter, TRUE);
+ real_filter = g_string_new ("(objectClass=)");
+ }
+ g_free (tmp);
+ }
+
+ /*g_print ("FILTER to use: LDAPFilter=> [%s] LDAPDn => [%s] SCOPE => [%d]\n",
+ real_filter ? real_filter->str : NULL,
+ real_dn ? real_dn : map->base_dn, real_scope);*/
+ model = gda_data_model_ldap_new (map->ldap_cnc,
+ real_dn ? real_dn : map->base_dn,
+ real_filter ? real_filter->str : NULL,
+ map->attributes, real_scope);
+ if (real_filter)
+ g_string_free (real_filter, TRUE);
+ g_free (real_dn);
+ }
+ else
+ model = gda_data_model_ldap_new (map->ldap_cnc, map->base_dn, map->filter,
+ map->attributes, map->scope);
+
+ return model;
+}
+
+/**
+ * gda_ldap_connection_declare_table:
+ * @cnc: a #GdaLdapConnection
+ * @table_name: a table name, not %NULL
+ * @base_dn: (allow-none): the base DN of the LDAP search, or %NULL for @cnc's own base DN
+ * @filter: (allow-none): the search filter of the LDAP search, or %NULL for a default filter of "(ObjectClass=*)"
+ * @attributes: (allow-none): the search attributes of the LDAP search, or %NULL if only the DN is required
+ * @scope: the search scope of the LDAP search
+ * @error: a place to store errors, or %NULL
+ *
+ * Declare a virtual table based on an LDAP search.
+ *
+ * The @filter argument, if not %NULL, must be a valid LDAP filter string (including the opening and
+ * closing parenthesis).
+ *
+ * The @attribute, if not %NULL, is a list of comma separated LDAP entry attribute names. For each attribute
+ * it is also possible to specify a requested #GType, and how to behave in case of multi valued attributes
+ * So the general format for an attribute is:
+ * "<attribute name>[::<type>][::<muti value handler>]", where:
+ * <itemizedlist>
+ * <listitem><para>"::<type>" is optional, see gda_g_type_from_string() for more
+ * information about valie values for <type></para></listitem>
+ * <listitem><para>"::<muti value handler>" is also optional and specifies how a multi
+ * values attributed is treated. The possibilities for <muti value handler> are:
+ * <itemizedlist>
+ * <listitem><para>"NULL" or "0": a NULL value will be returned</para></listitem>
+ * <listitem><para>"CSV": a comma separated value with all the values of the attribute will be
+ * returned. This only works for G_TYPE_STRING attribute types.</para></listitem>
+ * <listitem><para>"MULT" of "*": a row will be returned for each value of the attribute, effectively
+ * multiplying the number of returned rows</para></listitem>
+ * <listitem><para>"1": only the first vakue of the attribute will be used, the other values ignored</para></listitem>
+ * <listitem><para>"CONCAT": the attributes' values are concatenated (with a newline char between each value)</para></listitem>
+ * <listitem><para>"ERROR": an error value will be returned (this is the default behaviour)</para></listitem>
+ * </itemizedlist>
+ * </para></listitem>
+ * </itemizedlist>
+ *
+ * After each attribute
+ * name (and before the comma for the next attribute if any), it is possible to specify the #GType type using
+ * the "::<type>" syntax (see gda_g_type_from_string() for more information).
+ *
+ * The following example specifies the "uidNumber" attribute to be returned as a string, the "mail" attribute,
+ * and the "objectClass" attribute to be handled as one row per value of that attribute:
+ * <programlisting>"uidNumber::string,mail,objectClass::*"</programlisting>
+ *
+ * Returns: %TRUE if no error occurred
+ *
+ * Since: 4.2.8
+ */
+gboolean
+gda_ldap_connection_declare_table (GdaLdapConnection *cnc, const gchar *table_name,
+ const gchar *base_dn, const gchar *filter,
+ const gchar *attributes, GdaLdapSearchScope scope,
+ GError **error)
+{
+ LdapTableMap *map;
+ g_return_val_if_fail (GDA_IS_LDAP_CONNECTION (cnc), FALSE);
+ g_return_val_if_fail (table_name && *table_name, FALSE);
+
+ map = g_new0 (LdapTableMap, 1);
+ GDA_VCONNECTION_DATA_MODEL_SPEC (map)->data_model = NULL;
+ GDA_VCONNECTION_DATA_MODEL_SPEC (map)->create_columns_func = (GdaVconnectionDataModelCreateColumnsFunc) _ldap_create_columns_func;
+ GDA_VCONNECTION_DATA_MODEL_SPEC (map)->create_model_func = NULL;
+ GDA_VCONNECTION_DATA_MODEL_SPEC (map)->create_filter_func = _ldap_table_create_filter;
+ GDA_VCONNECTION_DATA_MODEL_SPEC (map)->create_filtered_model_func = _ldap_table_create_model_func;
+ map->ldap_cnc = g_object_ref (cnc);
+ map->table_name = gda_sql_identifier_quote (table_name, GDA_CONNECTION (cnc), NULL, TRUE, FALSE);
+ map->filters_hash = NULL;
+ if (base_dn)
+ map->base_dn = g_strdup (base_dn);
+ if (filter)
+ map->filter = g_strdup (filter);
+ if (attributes)
+ map->attributes = g_strdup (attributes);
+ map->scope = scope ? scope : GDA_LDAP_SEARCH_BASE;
+
+ cnc->priv->maps = g_slist_append (cnc->priv->maps, map);
+ if (!gda_vconnection_data_model_add (GDA_VCONNECTION_DATA_MODEL (cnc),
+ (GdaVconnectionDataModelSpec*) map,
+ (GDestroyNotify) _ldap_table_map_free, table_name, error)) {
+ cnc->priv->maps = g_slist_remove (cnc->priv->maps, map);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ * gda_ldap_connection_undeclare_table:
+ * @cnc: a #GdaLdapConnection
+ * @table_name: a table name, not %NULL
+ * @error: a place to store errors, or %NULL
+ *
+ * Remove a table which has been declared using gda_ldap_connection_declare_table().
+ *
+ * Returns: %TRUE if no error occurred
+ *
+ * Since: 4.2.8
+ */
+gboolean
+gda_ldap_connection_undeclare_table (GdaLdapConnection *cnc, const gchar *table_name, GError **error)
+{
+ GdaVconnectionDataModelSpec *specs;
+ g_return_val_if_fail (GDA_IS_LDAP_CONNECTION (cnc), FALSE);
+ g_return_val_if_fail (table_name && *table_name, FALSE);
+
+ specs = gda_vconnection_data_model_get (GDA_VCONNECTION_DATA_MODEL (cnc), table_name);
+ if (specs && ! g_slist_find (cnc->priv->maps, specs)) {
+ g_set_error (error, 0, 0,
+ _("Can't remove non LDAP virtual table"));
+ return FALSE;
+ }
+ return gda_vconnection_data_model_remove (GDA_VCONNECTION_DATA_MODEL (cnc), table_name, error);
+}
+
+/**
+ * gda_ldap_connection_describe_table:
+ * @cnc: a #GdaLdapConnection
+ * @table_name: a table name, not %NULL
+ * @out_base_dn: (allow-none) (transfer none): a place to store the LDAP search base DN, or %NULL
+ * @out_filter: (allow-none) (transfer none): a place to store the LDAP search filter, or %NULL
+ * @out_attributes: (allow-none) (transfer none): a place to store the LDAP search attributes, or %NULL
+ * @out_scope: (allow-none) (transfer none): a place to store the LDAP search scope, or %NULL
+ * @error: a place to store errors, or %NULL
+ *
+ * Get information about a virtual table, the information which has been passed to
+ * gda_ldap_connection_declare_table() when the table was created.
+ *
+ * Returns: %TRUE if no error occurred
+ *
+ * Since: 4.2.8
+ */
+gboolean
+gda_ldap_connection_describe_table (GdaLdapConnection *cnc, const gchar *table_name,
+ const gchar **out_base_dn, const gchar **out_filter,
+ const gchar **out_attributes,
+ GdaLdapSearchScope *out_scope, GError **error)
+{
+ GdaVconnectionDataModelSpec *specs;
+ LdapTableMap *map;
+
+ if (out_base_dn)
+ *out_base_dn = NULL;
+ if (out_filter)
+ *out_filter = NULL;
+ if (out_attributes)
+ *out_attributes = NULL;
+ if (out_scope)
+ *out_scope = GDA_LDAP_SEARCH_BASE;
+
+ g_return_val_if_fail (GDA_IS_LDAP_CONNECTION (cnc), FALSE);
+ g_return_val_if_fail (table_name && *table_name, FALSE);
+
+ specs = gda_vconnection_data_model_get (GDA_VCONNECTION_DATA_MODEL (cnc), table_name);
+ if (specs && ! g_slist_find (cnc->priv->maps, specs)) {
+ g_set_error (error, 0, 0,
+ _("Can't describe non LDAP virtual table"));
+ return FALSE;
+ }
+
+ if (!specs) {
+ g_set_error (error, 0, 0,
+ _("Unknown LDAP virtual table"));
+ return FALSE;
+ }
+
+ map = (LdapTableMap*) specs;
+ if (out_base_dn)
+ *out_base_dn = map->base_dn;
+ if (out_filter)
+ *out_filter = map->filter;
+ if (out_attributes)
+ *out_attributes = map->attributes;
+ if (out_scope)
+ *out_scope = map->scope;
+ return TRUE;
+}
+
+static void
+gda_ldap_attribute_free (GdaLdapAttribute *attr)
+{
+ if (attr) {
+ gint i;
+ g_free (attr->attr_name);
+ for (i = 0; attr->values[i]; i++)
+ gda_value_free (attr->values[i]);
+ g_free (attr->values);
+ }
+}
+
+/**
+ * gda_ldap_entry_free:
+ * @entry: (transfer full): a #GdaLdapEntry pointer
+ *
+ * Frees @entry
+ *
+ * Since: 4.2.8
+ */
+void
+gda_ldap_entry_free (GdaLdapEntry *entry)
+{
+ if (entry) {
+ g_free (entry->dn);
+ if (entry->attributes) {
+ gint i;
+ for (i = 0; entry->attributes[i]; i++)
+ gda_ldap_attribute_free (entry->attributes[i]);
+ g_free (entry->attributes);
+ }
+ if (entry->attributes_hash)
+ g_hash_table_destroy (entry->attributes_hash);
+ g_free (entry);
+ }
+}
+
+/* proxy declaration */
+GdaLdapEntry *_gda_ldap_describe_entry (GdaLdapConnection *cnc, const gchar *dn, GError **error);
+GdaLdapEntry **_gda_ldap_get_entry_children (GdaLdapConnection *cnc, const gchar *dn, gchar **attributes, GError **error);
+
+/**
+ * gda_ldap_describe_entry:
+ * @cnc: a #GdaLdapConnection connection
+ * @dn: (allow-none): the Distinguished Name of the LDAP entry to describe
+ * @error: (allow-none): a place to store errors, or %NULL
+ *
+ * Describes the LDAP entry which DN is @dn. If @dn is %NULL, then the top entry (as specified when
+ * the LDAP connection was opened) is described.
+ *
+ * Returns: (transfer full) (Free-function: gda_ldap_entry_free): a new #GdaLdapEntry, or %NULL if an error occurred or if the @dn entry does not exist
+ *
+ * Since: 4.2.8
+ */
+GdaLdapEntry *
+gda_ldap_describe_entry (GdaLdapConnection *cnc, const gchar *dn, GError **error)
+{
+ g_return_val_if_fail (GDA_IS_LDAP_CONNECTION (cnc), FALSE);
+
+ return _gda_ldap_describe_entry (cnc, dn, error);
+}
+
+/**
+ * gda_ldap_get_entry_children:
+ * @cnc: a #GdaLdapConnection connection
+ * @dn: (allow-none): the Distinguished Name of the LDAP entry to get children from
+ * @attributes: (allow-none) (array zero-terminated=1) (element-type gchar*): a %NULL terminated array of attributes to fetch for each child, or %NULL if no attribute is requested
+ * @error: (allow-none): a place to store errors, or %NULL
+ *
+ * Get the list of children entries for the LDAP entry which DN is @dn. If the @dn entry does not have any
+ * child, then this function returns an array which first element is %NULL.
+ *
+ * If @dn is %NULL, then the top entry (as specified when the LDAP connection was opened) is used.
+ *
+ * Returns: (transfer full) (element_type GdaLdapEntry) (array zero-terminated=1): a %NULL terminated array of #GdaLdapEntry for each child entry, or %NULL if an error occurred or if the @dn entry does not exist
+ *
+ * Since: 4.2.8
+ */
+GdaLdapEntry **
+gda_ldap_get_entry_children (GdaLdapConnection *cnc, const gchar *dn, gchar **attributes, GError **error)
+{
+ g_return_val_if_fail (GDA_IS_LDAP_CONNECTION (cnc), FALSE);
+
+ return _gda_ldap_get_entry_children (cnc, dn, attributes, error);
+}
+
+gchar **_gda_ldap_dn_split (const gchar *dn, gboolean all);
+
+/**
+ * gda_ldap_dn_split
+ * @dn: a Distinguished Name string
+ * @all: set to %FALSE to split @dn into its RND and its parent DN, or %TRUE to completely split @dn
+ *
+ * Splits @dn into its components.
+ *
+ * Returns: (transfer full) (Free-function: g_strfreev): a %NULL terminated array containing the DN parts (free using g_strfreev()), or %NULL if an error occurred because @dn is not a valid DN expression
+ *
+ * Since: 4.2.8
+ */
+gchar **
+gda_ldap_dn_split (const gchar *dn, gboolean all)
+{
+ return _gda_ldap_dn_split (dn, all);
+}
+
+gboolean _gda_ldap_is_dn (const gchar *dn);
+
+/**
+ * gda_ldap_is_dn:
+ * @dn: a Distinguished Name string
+ *
+ * Tells if @dn represents a distinguished name (it only checks for the syntax, not for
+ * the actual existence of the entry with that distinguished name).
+ *
+ * Returns: %TRUE if @dn is a valid representation of a distinguished name
+ *
+ * Since: 4.2.8
+ */
+gboolean
+gda_ldap_is_dn (const gchar *dn)
+{
+ return _gda_ldap_is_dn (dn);
+}
+
+const gchar *_gda_ldap_get_base_dn (GdaLdapConnection *cnc);
+
+/**
+ * gda_ldap_connection_get_base_dn:
+ * @cnc: a #GdaLdapConnection
+ *
+ * Get the base DN which was used when the LDAP connection was opened
+ *
+ * Returns: the base DN, or %NULL
+ *
+ * Since: 4.2.8
+ */
+const gchar *
+gda_ldap_connection_get_base_dn (GdaLdapConnection *cnc)
+{
+ g_return_val_if_fail (GDA_IS_LDAP_CONNECTION (cnc), NULL);
+
+ return _gda_ldap_get_base_dn (cnc);
+}
+
+GdaLdapClass *_gda_ldap_get_class_info (GdaLdapConnection *cnc, const gchar *classname);
+/**
+ * gda_ldap_get_class_info:
+ * @cnc: a #GdaLdapConnection
+ * @classname: an LDAP class name
+ *
+ * Get information about an LDAP class
+ *
+ * Returns: (transfer none): a #GdaLdapClass
+ *
+ * Since: 4.2.8
+ */
+GdaLdapClass*
+gda_ldap_get_class_info (GdaLdapConnection *cnc, const gchar *classname)
+{
+ g_return_val_if_fail (GDA_IS_LDAP_CONNECTION (cnc), NULL);
+
+ return _gda_ldap_get_class_info (cnc, classname);
+}
+
+const GSList *_gda_ldap_get_top_classes (GdaLdapConnection *cnc);
+/**
+ * gda_ldap_get_top_classes:
+ * @cnc: a #GdaLdapConnection
+ *
+ * get a list of the top level LDAP classes (ie. classes which don't have any parent)
+ *
+ * Returns: (transfer none) (element-type GdaLdapClass): a list of #GdaLdapClass pointers (don't modify it)
+ *
+ * Since: 4.2.8
+ */
+const GSList *
+gda_ldap_get_top_classes (GdaLdapConnection *cnc)
+{
+ g_return_val_if_fail (GDA_IS_LDAP_CONNECTION (cnc), NULL);
+
+ return _gda_ldap_get_top_classes (cnc);
+}
diff --git a/libgda/sqlite/virtual/gda-ldap-connection.h b/libgda/sqlite/virtual/gda-ldap-connection.h
new file mode 100644
index 0000000..d792486
--- /dev/null
+++ b/libgda/sqlite/virtual/gda-ldap-connection.h
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2011 The GNOME Foundation.
+ *
+ * AUTHORS:
+ * Vivien Malerba <malerba gnome-db org>
+ *
+ * This Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GDA_LDAP_CONNECTION_H__
+#define __GDA_LDAP_CONNECTION_H__
+
+#include <virtual/gda-vconnection-data-model.h>
+#include <libgda/gda-data-model-ldap.h>
+
+#define GDA_TYPE_LDAP_CONNECTION (gda_ldap_connection_get_type())
+#define GDA_LDAP_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, GDA_TYPE_LDAP_CONNECTION, GdaLdapConnection))
+#define GDA_LDAP_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, GDA_TYPE_LDAP_CONNECTION, GdaLdapConnectionClass))
+#define GDA_IS_LDAP_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, GDA_TYPE_LDAP_CONNECTION))
+#define GDA_IS_LDAP_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDA_TYPE_LDAP_CONNECTION))
+
+G_BEGIN_DECLS
+
+typedef struct _GdaLdapConnection GdaLdapConnection;
+typedef struct _GdaLdapConnectionClass GdaLdapConnectionClass;
+typedef struct _GdaLdapConnectionPrivate GdaLdapConnectionPrivate;
+
+struct _GdaLdapConnection {
+ GdaVconnectionDataModel parent;
+ GdaLdapConnectionPrivate *priv;
+};
+
+struct _GdaLdapConnectionClass {
+ GdaVconnectionDataModelClass parent_class;
+
+ /*< private >*/
+ /* Padding for future expansion */
+ void (*_gda_reserved1) (void);
+ void (*_gda_reserved2) (void);
+ void (*_gda_reserved3) (void);
+ void (*_gda_reserved4) (void);
+};
+
+/**
+ * SECTION:gda-ldap-connection
+ * @short_description: LDAP connection objects
+ * @title: GdaLdapConnection
+ * @stability: Stable
+ * @see_also:
+ *
+ * This is a connection, as opened by the LDAP provider. Use
+ * gda_connection_open_from_string() or gda_connection_open_from_dsn() to create
+ * a #GdaLdapConnection connection.
+ *
+ * Warning: if you create a #GdaLdapConnection using g_object_new(), then the resulting
+ * object won't be functionnal.
+ *
+ * A #GdaLdapConnection is a virtual connection which accepts any of SQLite's SQL dialect.
+ * However, some SQL commands have been added to manipulate virtual tables mapped to
+ * LDAP searches. These commands are:
+ * <itemizedlist>
+ * <listitem>
+ * <para>The CREATE LDAP TABLE: <synopsis>CREATE LDAP TABLE <table name> [BASE=<base DN>] [FILTER=<LDAP filter>] [ATTRIBUTES=<LDAP attributes>] [SCOPE=<search scope>]</synopsis>
+ * </para>
+ * <para>Each of the BASE, FILTER, ATTRIBUTES and SCOPE specifications is optional. Use this command to declare a table, for example: <programlisting>CREATE LDAP TABLE users FILTER='(cn=*doe*)' SCOPE= 'SUBTREE';</programlisting>.
+ * The allowed SCOPE values are: 'BASE', 'ONELEVEL' and 'SUBTREE'.
+ * </para>
+ * <para>See the <link linkend="gda-ldap-connection-declare-table">gda_ldap_connection_declare_table()</link>
+ * for more information about the ATTRIBUTES syntax.
+ * </para>
+ * </listitem>
+ * <listitem>
+ * <para>The DROP LDAP TABLE: <synopsis>DROP LDAP TABLE <table name></synopsis>
+ * </para>
+ * <para>Use this command to undeclare a table, for example: <programlisting>DROP LDAP TABLE users;</programlisting> Note that it is also possible to use the normal command to remove a table: <programlisting>DROP TABLE users;</programlisting>
+ * </para>
+ * </listitem>
+ * <listitem>
+ * <para>The ALTER LDAP TABLE: <synopsis>ALTER LDAP TABLE <table name></synopsis> or
+ * <synopsis>ALTER LDAP TABLE <table name> [BASE=<base DN>] [FILTER=<LDAP filter>] [ATTRIBUTES=<LDAP attributes>] [SCOPE=<search scope>]</synopsis>
+ * </para>
+ * <para>Use this command to modify the definition of a virtual table, for example: <programlisting>ALTER LDAP TABLE users FILTER='(cn=*doe*)' SCOPE='BASE';</programlisting> If no argument is specified after the table name, then
+ * the definition of the virtual table is returned instead. When altering the virtual table, only the
+ * specified parameters are altered (ie. you don't need to repeat the parameters you don't want to
+ * be modified)
+ * </para>
+ * </listitem>
+ * <listitem>
+ * <para>The DESCRIBE LDAP TABLE: <synopsis>DESCRIBE LDAP TABLE <table name></synopsis>
+ * </para>
+ * <para>Use this command to get the definition of the virtual table.
+ * </para>
+ * </listitem>
+ * </itemizedlist>
+ *
+ * Each "LDAP" table can then be used like any other table, but you should keep in mind that &LIBGDA;
+ * can optimize the LDAP search command executed when the table's data is actually read if you use
+ * a "WHERE" clause which involves a search criteria on an LDAP attribute. For example the following
+ * SQL: <programlisting>SELECT * FROM users WHERE cn MATCH '%doe%';</programlisting> will actually
+ * be optimized by requesting an LDAP search with the filter <programlisting>(cn=*doe*)</programlisting>
+ * Optimizations can be done on MATCH, =, <, <=, > and >= operators, the LIKE operator
+ * will not be optimized.
+ *
+ * However a command like <programlisting>SELECT * FROM users WHERE cn MATCH '%doe%' OR uid=123;</programlisting>
+ * can't be optimized (because of the "OR") whereas the
+ * <programlisting>SELECT * FROM users WHERE cn MATCH '%doe%' AND uid=123;</programlisting>
+ * will be optimized because of the "AND".
+ */
+
+GType gda_ldap_connection_get_type (void) G_GNUC_CONST;
+
+const gchar *gda_ldap_connection_get_base_dn (GdaLdapConnection *cnc);
+gboolean gda_ldap_connection_declare_table (GdaLdapConnection *cnc, const gchar *table_name,
+ const gchar *base_dn, const gchar *filter,
+ const gchar *attributes, GdaLdapSearchScope scope,
+ GError **error);
+gboolean gda_ldap_connection_undeclare_table (GdaLdapConnection *cnc, const gchar *table_name, GError **error);
+
+gboolean gda_ldap_connection_describe_table (GdaLdapConnection *cnc, const gchar *table_name,
+ const gchar **out_base_dn, const gchar **out_filter,
+ const gchar **out_attributes,
+ GdaLdapSearchScope *out_scope, GError **error);
+
+
+/**
+ * GdaLdapAttribute:
+ * @attr_name: the name of the attribute
+ * @nb_values: the number of values in @values, or %0
+ * @values: (allow-none) (array length=nb_values): the attribute' values as #GValue values, (terminated by a %NULL)
+ *
+ * This structure holds information about the values of a single attribute (of a single LDAP entry).
+ */
+typedef struct {
+ gchar *attr_name;
+ guint nb_values;
+ GValue **values;
+} GdaLdapAttribute;
+
+/**
+ * GdaLdapEntry:
+ * @dn: the Distinguished Name of the entry
+ * @nb_attributes: the number of attributes in @attributes, or %0
+ * @attributes: (allow-none) (array length=nb_attributes): the entry's attributes, (terminated by a %NULL)
+ * @attributes_hash: a hash table where the key is an attribute name, and the value the correcponding #GdaLdapAttribute
+ *
+ * This structure holds information about the attributes of a single LDAP entry.
+ */
+typedef struct {
+ gchar *dn;
+ guint nb_attributes;
+ GdaLdapAttribute **attributes;
+ GHashTable *attributes_hash;
+} GdaLdapEntry;
+
+void gda_ldap_entry_free (GdaLdapEntry *entry);
+GdaLdapEntry *gda_ldap_describe_entry (GdaLdapConnection *cnc, const gchar *dn, GError **error);
+GdaLdapEntry **gda_ldap_get_entry_children (GdaLdapConnection *cnc, const gchar *dn,
+ gchar **attributes, GError **error);
+
+gchar **gda_ldap_dn_split (const gchar *dn, gboolean all);
+gboolean gda_ldap_is_dn (const gchar *dn);
+
+
+/**
+ * GdaLdapClassKind:
+ * @GDA_LDAP_CLASS_KIND_ABSTRACT: the LDAP class is an abstract class
+ * @GDA_LDAP_CLASS_KIND_STRUTURAL: the LDAP class is a structural class
+ * @GDA_LDAP_CLASS_KIND_AUXILIARY: the LDAP class is auxilliary
+ * @GDA_LDAP_CLASS_KIND_UNKNOWN: the LDAP class type is not known
+ *
+ * Defines the LDAP class type
+ */
+typedef enum {
+ GDA_LDAP_CLASS_KIND_ABSTRACT = 1,
+ GDA_LDAP_CLASS_KIND_STRUTURAL = 2,
+ GDA_LDAP_CLASS_KIND_AUXILIARY = 3,
+ GDA_LDAP_CLASS_KIND_UNKNOWN = 4
+} GdaLdapClassKind;
+
+/**
+ * GdaLdapClass:
+ * @oid: the OID of the class
+ * @nb_names: the number of values in @values (always >= 1)
+ * @names: all the class names
+ * @description: (allow-none): the class's description, or %NULL
+ * @kind: the class kind
+ * @obsolete: defines is LDAP class is obsolete
+ * @nb_req_attributes: the number of values in @req_attributes
+ * @req_attributes: names of required attributes in class
+ * @nb_opt_attributes: the number of values in @opt_attributes
+ * @opt_attributes: names of optional attributes in class
+ * @parents: #GSList of the parent classes (pointers to #GdaLdapClass)
+ * @children: #GSList of the children classes (pointers to #GdaLdapClass)
+ *
+ * Represents an LDAP declared class.
+ */
+typedef struct {
+ gchar *oid;
+ guint nb_names; /* always >= 1 */
+ gchar **names;
+ gchar *description;
+ GdaLdapClassKind kind;
+ gboolean obsolete;
+
+ guint nb_req_attributes;
+ gchar **req_attributes;
+ guint nb_opt_attributes;
+ gchar **opt_attributes;
+
+ GSList *parents; /* list of #LdapClass */
+ GSList *children; /* list of #LdapClass */
+} GdaLdapClass;
+
+GdaLdapClass *gda_ldap_get_class_info (GdaLdapConnection *cnc, const gchar *classname);
+const GSList *gda_ldap_get_top_classes (GdaLdapConnection *cnc);
+
+G_END_DECLS
+
+#endif
diff --git a/libgda/sqlite/virtual/gda-vprovider-data-model.c b/libgda/sqlite/virtual/gda-vprovider-data-model.c
index 833ccdf..9202207 100644
--- a/libgda/sqlite/virtual/gda-vprovider-data-model.c
+++ b/libgda/sqlite/virtual/gda-vprovider-data-model.c
@@ -245,6 +245,48 @@ static sqlite3_module Module = {
virtualRename /* Rename - Notification that the table will be given a new name */
};
+/*
+ * handle data model exceptions and return appropriate code
+ */
+static int
+handle_data_model_exception (sqlite3_vtab *pVtab, GdaDataModel *model)
+{
+ GError **exceptions;
+ gint i;
+ exceptions = gda_data_model_get_exceptions (model);
+ if (!exceptions)
+ return SQLITE_OK;
+
+ GError *trunc_error = NULL;
+ GError *fatal_error = NULL;
+ for (i = 0; exceptions [i]; i++) {
+ GError *e;
+ e = exceptions [i];
+ if ((e->domain == GDA_DATA_MODEL_ERROR) &&
+ (e->code == GDA_DATA_MODEL_TRUNCATED_ERROR))
+ trunc_error = e;
+ else {
+ fatal_error = e;
+ break;
+ }
+ }
+ if (fatal_error || trunc_error) {
+ GError *e;
+ e = fatal_error;
+ if (!e)
+ e = trunc_error;
+ if (pVtab->zErrMsg)
+ SQLITE3_CALL (sqlite3_free) (pVtab->zErrMsg);
+ pVtab->zErrMsg = SQLITE3_CALL (sqlite3_mprintf)
+ (e->message ? e->message : _("No detail"));
+ if (fatal_error)
+ return SQLITE_ERROR;
+ else
+ return SQLITE_IOERR_TRUNCATE;
+ }
+ return SQLITE_OK;
+}
+
static GdaConnection *
gda_vprovider_data_model_create_connection (GdaServerProvider *provider)
{
@@ -573,7 +615,7 @@ virtualDestroy (sqlite3_vtab *pVtab)
}
static int
-virtualOpen (sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor)
+virtualOpen (G_GNUC_UNUSED sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor)
{
VirtualCursor *cursor;
@@ -629,10 +671,8 @@ virtualNext (sqlite3_vtab_cursor *cur)
if (!gda_data_model_iter_move_next (cursor->iter)) {
if (gda_data_model_iter_is_valid (cursor->iter))
return SQLITE_IOERR;
- else
- return SQLITE_OK;
}
- return SQLITE_OK;
+ return handle_data_model_exception (cur->pVtab, cursor->model);
}
static int
@@ -719,10 +759,14 @@ virtualRowid (sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid)
GValue *gvalue;
cvalue = gda_data_model_iter_get_value_at (cursor->iter, i);
gvalue = gda_row_get_value (grow, i);
- if (G_VALUE_TYPE (cvalue) != GDA_TYPE_NULL) {
- g_value_init (gvalue, G_VALUE_TYPE (cvalue));
- g_value_copy (cvalue, gvalue);
+ if (cvalue) {
+ if (G_VALUE_TYPE (cvalue) != GDA_TYPE_NULL) {
+ g_value_init (gvalue, G_VALUE_TYPE (cvalue));
+ g_value_copy (cvalue, gvalue);
+ }
}
+ else
+ gda_row_invalidate_value (grow, gvalue);
}
hid = g_new (gint64, 1);
@@ -866,7 +910,8 @@ virtualFilter (sqlite3_vtab_cursor *pVtabCursor, int idxNum, const char *idxStr,
cursor->model = g_object_ref (vtable->td->real_model);
gda_data_model_iter_move_next (cursor->iter);
- return SQLITE_OK;
+
+ return handle_data_model_exception (pVtabCursor->pVtab, cursor->model);
}
#ifdef GDA_DEBUG_VIRTUAL
@@ -1016,7 +1061,6 @@ virtualBestIndex (sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo)
#endif
}
-
return SQLITE_OK;
}
@@ -1234,7 +1278,7 @@ virtualUpdate (sqlite3_vtab *tab, int nData, sqlite3_value **apData, sqlite_int6
sql = gda_statement_to_sql (stmt, NULL, NULL);
g_print ("SQL: [%s] ", sql);
g_free (sql);
- sql = gda_statement_to_sql_extended (stmt, cnc, params, GDA_STATEMENT_SQL_PRETTY, NULL, &lerror);
+ sql = gda_statement_to_sql_extended (stmt, cnc, vtable->td->modif_params [ptype], GDA_STATEMENT_SQL_PRETTY, NULL, &lerror);
if (sql) {
g_print ("With params: [%s]\n", sql);
g_free (sql);
@@ -1342,7 +1386,7 @@ virtualUpdate (sqlite3_vtab *tab, int nData, sqlite3_value **apData, sqlite_int6
}
static int
-virtualBegin (sqlite3_vtab *tab)
+virtualBegin (G_GNUC_UNUSED sqlite3_vtab *tab)
{
TRACE (tab, NULL);
/* no documentation currently available, don't do anything */
@@ -1381,7 +1425,7 @@ virtualRollback (G_GNUC_UNUSED sqlite3_vtab *tab)
}
static int
-virtualRename (sqlite3_vtab *pVtab, G_GNUC_UNUSED const char *zNew)
+virtualRename (G_GNUC_UNUSED sqlite3_vtab *pVtab, G_GNUC_UNUSED const char *zNew)
{
TRACE (pVtab, NULL);
/* not yet analysed and implemented */
diff --git a/libgda/sqlite/virtual/libgda-virtual.h b/libgda/sqlite/virtual/libgda-virtual.h.in
similarity index 94%
copy from libgda/sqlite/virtual/libgda-virtual.h
copy to libgda/sqlite/virtual/libgda-virtual.h.in
index 2986ccf..1e25751 100644
--- a/libgda/sqlite/virtual/libgda-virtual.h
+++ b/libgda/sqlite/virtual/libgda-virtual.h.in
@@ -1,5 +1,5 @@
-/* GDA library
- * Copyright (C) 2007 The GNOME Foundation.
+/*
+ * Copyright (C) 2007 - 2011 The GNOME Foundation.
*
* AUTHORS:
* Vivien Malerba <malerba gnome-db org>
@@ -31,4 +31,6 @@
#include <virtual/gda-vconnection-data-model.h>
#include <virtual/gda-vconnection-hub.h>
+ LIBGDA_LDAP_VINC@
+
#endif
diff --git a/m4/ldap.m4 b/m4/ldap.m4
new file mode 100644
index 0000000..09da463
--- /dev/null
+++ b/m4/ldap.m4
@@ -0,0 +1,181 @@
+dnl -*- mode: autoconf -*-
+dnl Copyright 2010 Vivien Malerba
+dnl
+dnl SYNOPSIS
+dnl
+dnl LDAP_CHECK([libdirname])
+dnl
+dnl [libdirname]: defaults to "lib". Can be overridden by the --with-bdb-libdir-name option
+dnl
+dnl DESCRIPTION
+dnl
+dnl This macro tries to find the LDAP libraries and header files
+dnl
+dnl It defines two options:
+dnl --with-ldap=yes/no/<directory>
+dnl --with-ldap-libdir-name=<dir. name>
+dnl
+dnl If the 1st option is "yes" then the macro in several well known directories
+dnl
+dnl If the 1st option is "no" then the macro does not attempt at locating the
+dnl LDAP package
+dnl
+dnl If the 1st option is a directory name, then the macro tries to locate the LDAP package
+dnl in the specified directory.
+dnl
+dnl If the macro has to try to locate the LDAP package in one or more directories, it will
+dnl try to locate the header files in $dir/include and the library files in $dir/lib, unless
+dnl the second option is used to specify a directory name to be used instead of "lib" (for
+dnl example lib64).
+dnl
+dnl USED VARIABLES
+dnl
+dnl $linklibext: contains the library suffix (like ".so"). If not specified ".so" is used.
+dnl $platform_win32: contains "yes" on Windows platforms. If not specified, assumes "no"
+dnl
+dnl
+dnl DEFINED VARIABLES
+dnl
+dnl This macro always calls:
+dnl
+dnl AC_SUBST(LDAP_LIBS)
+dnl AC_SUBST(LDAP_LIB)
+dnl AC_SUBST(LDAP_CFLAGS)
+dnl AC_SUBST(LIBGDA_LDAP_INC)
+dnl AC_SUBST(LIBGDA_LDAP_INC2)
+dnl AC_SUBST(LIBGDA_LDAP_VINC)
+dnl AC_SUBST(LIBGDA_LDAP_TYPE)
+dnl AC_SUBST(LIBGDA_LDAP_TYPE2)
+dnl AC_SUBST(LIBGDA_LDAP_TYPE3)
+dnl ldap_found=yes/no
+dnl
+dnl and if the ldap package is found:
+dnl
+dnl AM_CONDITIONAL(LDAP, true)
+dnl
+dnl
+dnl LICENSE
+dnl
+dnl This file is free software; the author(s) gives unlimited
+dnl permission to copy and/or distribute it, with or without
+dnl modifications, as long as this notice is preserved.
+dnl
+
+m4_define([_LDAP_CHECK_INTERNAL],
+[
+ AC_BEFORE([AC_PROG_LIBTOOL],[$0])dnl setup libtool first
+ AC_BEFORE([AM_PROG_LIBTOOL],[$0])dnl setup libtool first
+ AC_BEFORE([LT_INIT],[$0])dnl setup libtool first
+
+ ldap_loclibdir=$1
+ if test "x$ldap_loclibdir" = x
+ then
+ if test "x$platform_win32" = xyes
+ then
+ ldap_loclibdir=bin
+ else
+ ldap_loclibdir=lib
+ fi
+ fi
+
+ # determine if Ldap should be searched for
+ # and use pkg-config if the "yes" option is used
+ ldap_found=no
+ try_ldap=true
+ LDAP_LIBS=""
+ ldap_test_dir="/usr /usr/local /local"
+ AC_ARG_WITH(ldap,
+ AS_HELP_STRING([--with-ldap[=@<:@yes/no/<directory>@:>@]],
+ [Locate LDAP client library]),[
+ if test $withval = no
+ then
+ try_ldap=false
+ elif test $withval != yes
+ then
+ ldap_test_dir=$withval
+ fi])
+ AC_ARG_WITH(ldap-libdir-name,
+ AS_HELP_STRING([--with-ldap-libdir-name[=@<:@<dir. name>@:>@]],
+ [Locate LDAP library file, related to the prefix specified from --with-ldap]),
+ [ldap_loclibdir=$withval])
+
+ # try to locate files
+ if test $try_ldap = true
+ then
+ if test "x$linklibext" = x
+ then
+ ldap_libext=".so"
+ else
+ ldap_libext="$linklibext"
+ fi
+ ldapdir=""
+ for d in $ldap_test_dir
+ do
+ ldapdir=""
+ AC_MSG_CHECKING([for LDAP files in $d])
+
+ if test -f $d/include/ldap.h
+ then
+ save_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS -I$d/include"
+ save_LIBS="$LIBS"
+ LIBS="$LIBS -L$d/$ldap_loclibdir -lldap -llber"
+ AC_LINK_IFELSE([[
+#include <ldap.h>
+#include <lber.h>
+#include <ldap_schema.h>
+int main() {
+ printf("%p,%p", ldap_initialize, ldap_str2attributetype);
+ printf("%p", ber_free);
+ return 0;
+}
+]],
+ ldapdir=$d)
+ CFLAGS="$save_CFLAGS"
+ LIBS="$save_LIBS"
+ fi
+
+ if test x$ldapdir != x
+ then
+ AC_MSG_RESULT([found])
+ LDAP_CFLAGS=-I${ldapdir}/include
+ LDAP_LIBS="-L${ldapdir}/$ldap_loclibdir -lldap -llber"
+ break
+ else
+ AC_MSG_RESULT([not found])
+ fi
+ done
+
+ if test "x$LDAP_LIBS" = x
+ then
+ AC_MSG_NOTICE([LDAP backend not used])
+ else
+ LIBGDA_LDAP_INC="#include <libgda/gda-data-model-ldap.h>"
+ LIBGDA_LDAP_INC2="#include <libgda/gda-tree-mgr-ldap.h>"
+ LIBGDA_LDAP_VINC="#include <virtual/gda-ldap-connection.h>"
+ LIBGDA_LDAP_TYPE="gda_data_model_ldap_get_type"
+ LIBGDA_LDAP_TYPE2="gda_ldap_connection_get_type"
+ LIBGDA_LDAP_TYPE3="gda_tree_mgr_ldap_get_type"
+ ldap_found=yes
+ fi
+ fi
+
+ AM_CONDITIONAL(LDAP,[test "$ldap_found" = "yes"])
+ AC_SUBST(LDAP_LIBS)
+ AC_SUBST(LDAP_CFLAGS)
+ AC_SUBST(LIBGDA_LDAP_INC)
+ AC_SUBST(LIBGDA_LDAP_INC2)
+ AC_SUBST(LIBGDA_LDAP_VINC)
+ AC_SUBST(LIBGDA_LDAP_TYPE)
+ AC_SUBST(LIBGDA_LDAP_TYPE2)
+ AC_SUBST(LIBGDA_LDAP_TYPE3)
+])
+
+
+dnl Usage:
+dnl LDAP_CHECK([libdirname])
+
+AC_DEFUN([LDAP_CHECK],
+[
+ _LDAP_CHECK_INTERNAL([$1])
+])
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 3ed1fe0..9e15c89 100755
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -20,6 +20,7 @@ libgda/gda-data-model-dir.c
libgda/gda-data-model-dsn-list.c
libgda/gda-data-model-import.c
libgda/gda-data-model-iter.c
+libgda/gda-data-model-ldap.c
libgda/gda-data-proxy.c
libgda/gda-data-select.c
libgda/gda-holder.c
@@ -36,6 +37,7 @@ libgda/gda-sql-builder.c
libgda/gda-statement.c
libgda/gda-tree-mgr-columns.c
libgda/gda-tree-mgr-label.c
+libgda/gda-tree-mgr-ldap.c
libgda/gda-tree-mgr-schemas.c
libgda/gda-tree-mgr-select.c
libgda/gda-tree-mgr-tables.c
@@ -58,6 +60,7 @@ libgda/sqlite/gda-sqlite-provider.c
libgda/sqlite/gda-sqlite-pstmt.c
libgda/sqlite/gda-sqlite-recordset.c
libgda/sqlite/gda-sqlite-util.c
+libgda/sqlite/virtual/gda-ldap-connection.c
libgda/sqlite/virtual/gda-vconnection-data-model.c
libgda/sqlite/virtual/gda-vconnection-hub.c
libgda/sqlite/virtual/gda-virtual-connection.c
@@ -149,6 +152,12 @@ providers/jdbc/gda-jdbc-util.c
providers/jdbc/jdbc_specs_create_table.xml.in
providers/jdbc/jdbc_specs_dsn.xml.in
providers/jdbc/libmain.c
+providers/ldap/gda-ldap-provider.c
+providers/ldap/gda-ldap-util.c
+providers/ldap/gdaprov-data-model-ldap.c
+providers/ldap/ldap_specs_auth.xml.in
+providers/ldap/ldap_specs_dsn.xml.in
+providers/ldap/libmain.c
providers/mdb/gda-mdb-provider.c
providers/mdb/libmain.c
providers/mdb/mdb_specs_dsn.xml.in
@@ -276,8 +285,20 @@ tools/browser/data-manager/data-widget.c
tools/browser/data-manager/perspective-main.c
tools/browser/data-manager/ui-spec-editor.c
tools/browser/data-manager/xml-spec-editor.c
+tools/browser/ldap-browser/class-properties.c
+tools/browser/ldap-browser/entry-properties.c
+tools/browser/ldap-browser/filter-editor.c
+tools/browser/ldap-browser/hierarchy-view.c
+tools/browser/ldap-browser/ldap-browser-perspective.c
+tools/browser/ldap-browser/ldap-classes-page.c
+tools/browser/ldap-browser/ldap-entries-page.c
+tools/browser/ldap-browser/ldap-favorite-selector.c
+tools/browser/ldap-browser/ldap-search-page.c
+tools/browser/ldap-browser/mgr-ldap-classes.c
+tools/browser/ldap-browser/perspective-main.c
+tools/browser/ldap-browser/vtable-dialog.c
tools/browser/query-exec/perspective-main.c
-tools/browser/query-exec/query-console.c
+tools/browser/query-exec/query-console-page.c
tools/browser/query-exec/query-editor.c
tools/browser/query-exec/query-exec-perspective.c
tools/browser/query-exec/query-favorite-selector.c
diff --git a/providers/Makefile.am b/providers/Makefile.am
index 80aa813..b8fa6fe 100644
--- a/providers/Makefile.am
+++ b/providers/Makefile.am
@@ -38,6 +38,10 @@ if HAVE_LIBCRYPTO
GDA_SQLCIPHER_SERVER=sqlcipher
endif
+if LDAP
+GDA_LDAP_SERVER=ldap
+endif
+
SUBDIRS = \
reuseable \
sqlite \
@@ -50,6 +54,7 @@ SUBDIRS = \
$(GDA_JAVA_SERVER) \
$(GDA_ORACLE_SERVER) \
$(GDA_WEB_SERVER) \
- $(GDA_SQLCIPHER_SERVER)
+ $(GDA_SQLCIPHER_SERVER) \
+ $(GDA_LDAP_SERVER)
# $(GDA_FIREBIRD_SERVER)
diff --git a/providers/ldap/Makefile.am b/providers/ldap/Makefile.am
new file mode 100644
index 0000000..df31644
--- /dev/null
+++ b/providers/ldap/Makefile.am
@@ -0,0 +1,40 @@
+providerdir=$(libdir)/libgda-$(GDA_ABI_MAJOR_VERSION).$(GDA_ABI_MINOR_VERSION)/providers
+provider_LTLIBRARIES = libgda-ldap.la
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/libgda/sqlite \
+ -I$(top_srcdir)/libgda \
+ -I$(top_builddir) \
+ $(COREDEPS_CFLAGS) \
+ $(LDAP_CFLAGS)
+
+libgda_ldap_la_SOURCES = \
+ gdaprov-data-model-ldap.c \
+ gdaprov-data-model-ldap.h \
+ gda-ldap-provider.c \
+ gda-ldap-provider.h \
+ gda-ldap-util.c \
+ gda-ldap-util.h \
+ gda-ldap.h \
+ libmain.c
+
+libgda_ldap_la_LDFLAGS = -export-dynamic -module -avoid-version $(NO_UNDEFINED) $(LIBTOOL_PROV_EXPORT_OPTIONS)
+libgda_ldap_la_LIBADD = \
+ $(top_builddir)/libgda/libgda-5.0.la \
+ $(COREDEPS_LIBS) \
+ $(LDAP_LIBS)
+
+xmldir = $(datadir)/libgda-5.0
+xml_in_files = ldap_specs_dsn.xml.in ldap_specs_auth.xml.in
+
+ INTLTOOL_XML_RULE@
+
+xml_DATA = $(xml_in_files:.xml.in=.xml)
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = libgda-ldap-5.0.pc
+
+EXTRA_DIST = $(xml_in_files) libgda-ldap-5.0.pc.in
+DISTCLEANFILES = $(xml_DATA)
+
diff --git a/providers/ldap/gda-ldap-provider.c b/providers/ldap/gda-ldap-provider.c
new file mode 100644
index 0000000..7689191
--- /dev/null
+++ b/providers/ldap/gda-ldap-provider.c
@@ -0,0 +1,927 @@
+/*
+ * Copyright (C) 2011 The GNOME Foundation
+ *
+ * AUTHORS:
+ * Vivien Malerba <malerba gnome-db org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <glib/gi18n-lib.h>
+#include <virtual/gda-ldap-connection.h>
+#include <libgda/gda-connection-private.h>
+#include <libgda/gda-data-model-iter.h>
+#include <libgda/gda-util.h>
+#include <libgda/gda-data-model-array.h>
+#include <libgda/sql-parser/gda-sql-parser.h>
+#include "gda-ldap.h"
+#include "gda-ldap-provider.h"
+#include "gdaprov-data-model-ldap.h"
+#include "gda-ldap-util.h"
+
+static void gda_ldap_provider_class_init (GdaLdapProviderClass *klass);
+static void gda_ldap_provider_init (GdaLdapProvider *provider,
+ GdaLdapProviderClass *klass);
+static void gda_ldap_provider_finalize (GObject *object);
+
+static const gchar *gda_ldap_provider_get_name (GdaServerProvider *provider);
+static const gchar *gda_ldap_provider_get_version (GdaServerProvider *provider);
+static GdaConnection *gda_ldap_provider_create_connection (GdaServerProvider *provider);
+static gboolean gda_ldap_provider_open_connection (GdaServerProvider *provider, GdaConnection *cnc,
+ GdaQuarkList *params, GdaQuarkList *auth,
+ guint *task_id, GdaServerProviderAsyncCallback async_cb, gpointer cb_data);
+static GObject *gda_ldap_provider_statement_execute (GdaServerProvider *provider, GdaConnection *cnc,
+ GdaStatement *stmt, GdaSet *params,
+ GdaStatementModelUsage model_usage,
+ GType *col_types, GdaSet **last_inserted_row,
+ guint *task_id, GdaServerProviderExecCallback async_cb,
+ gpointer cb_data, GError **error);
+static const gchar *gda_ldap_provider_get_server_version (GdaServerProvider *provider,
+ GdaConnection *cnc);
+static const gchar *gda_ldap_provider_get_database (GdaServerProvider *provider, GdaConnection *cnc);
+
+static GObjectClass *parent_class = NULL;
+
+/*
+ * private connection data destroy
+ */
+static void gda_ldap_free_cnc_data (LdapConnectionData *cdata);
+
+/*
+ * GdaLdapProvider class implementation
+ */
+static void
+gda_ldap_provider_class_init (GdaLdapProviderClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GdaServerProviderClass *provider_class = GDA_SERVER_PROVIDER_CLASS (klass);
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ object_class->finalize = gda_ldap_provider_finalize;
+ provider_class->create_connection = gda_ldap_provider_create_connection;
+
+ provider_class->get_name = gda_ldap_provider_get_name;
+ provider_class->get_version = gda_ldap_provider_get_version;
+ provider_class->open_connection = gda_ldap_provider_open_connection;
+ provider_class->get_server_version = gda_ldap_provider_get_server_version;
+ provider_class->get_database = gda_ldap_provider_get_database;
+ provider_class->statement_execute = gda_ldap_provider_statement_execute;
+}
+
+static void
+gda_ldap_provider_init (G_GNUC_UNUSED GdaLdapProvider *pg_prv,
+ G_GNUC_UNUSED GdaLdapProviderClass *klass)
+{
+ /* nothing specific there */
+}
+
+static void
+gda_ldap_provider_finalize (GObject *object)
+{
+ GdaLdapProvider *pg_prv = (GdaLdapProvider *) object;
+
+ g_return_if_fail (GDA_IS_LDAP_PROVIDER (pg_prv));
+
+ /* chain to parent class */
+ parent_class->finalize(object);
+}
+
+GType
+gda_ldap_provider_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0)) {
+ static GStaticMutex registering = G_STATIC_MUTEX_INIT;
+ static GTypeInfo info = {
+ sizeof (GdaLdapProviderClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) gda_ldap_provider_class_init,
+ NULL, NULL,
+ sizeof (GdaLdapProvider),
+ 0,
+ (GInstanceInitFunc) gda_ldap_provider_init,
+ 0
+ };
+ g_static_mutex_lock (®istering);
+ if (type == 0)
+ type = g_type_register_static (GDA_TYPE_VPROVIDER_DATA_MODEL, "GdaLdapProvider", &info, 0);
+ g_static_mutex_unlock (®istering);
+ }
+
+ return type;
+}
+
+/*
+ * Get provider name request
+ */
+static const gchar *
+gda_ldap_provider_get_name (G_GNUC_UNUSED GdaServerProvider *provider)
+{
+ return LDAP_PROVIDER_NAME;
+}
+
+/*
+ * Get version request
+ */
+static const gchar *
+gda_ldap_provider_get_version (G_GNUC_UNUSED GdaServerProvider *provider)
+{
+ return PACKAGE_VERSION;
+}
+
+static GdaConnection *
+gda_ldap_provider_create_connection (GdaServerProvider *provider)
+{
+ GdaConnection *cnc;
+ g_return_val_if_fail (GDA_IS_LDAP_PROVIDER (provider), NULL);
+
+ cnc = g_object_new (GDA_TYPE_LDAP_CONNECTION, "provider", provider, NULL);
+
+ return cnc;
+}
+
+/*
+ * compute cache file's absolute name
+ */
+static gchar *
+compute_data_file_name (GdaQuarkList *params, gboolean is_cache, const gchar *data_type)
+{
+ /* real cache file name */
+ GString *string;
+ gchar *cfile, *evalue;
+ const gchar *base_dn;
+ const gchar *host;
+ const gchar *require_ssl;
+ const gchar *port;
+ gint rport;
+ gboolean use_ssl;
+
+ base_dn = gda_quark_list_find (params, "DB_NAME");
+ host = gda_quark_list_find (params, "HOST");
+ if (!host)
+ host = "127.0.0.1";
+ port = gda_quark_list_find (params, "PORT");
+ require_ssl = gda_quark_list_find (params, "USE_SSL");
+ use_ssl = (require_ssl && ((*require_ssl == 't') || (*require_ssl == 'T'))) ? TRUE : FALSE;
+ if (port && *port)
+ rport = atoi (port);
+ else {
+ if (use_ssl)
+ rport = LDAPS_PORT;
+ else
+ rport = LDAP_PORT;
+ }
+ string = g_string_new ("");
+ evalue = gda_rfc1738_encode (host);
+ g_string_append_printf (string, ",=%s", evalue);
+ g_free (evalue);
+ g_string_append_printf (string, ";PORT=%d", rport);
+ if (base_dn) {
+ evalue = gda_rfc1738_encode (base_dn);
+ g_string_append_printf (string, ";BASE_DN,=%s", evalue);
+ g_free (evalue);
+ }
+ evalue = g_compute_checksum_for_string (G_CHECKSUM_SHA1, string->str, -1);
+ g_string_free (string, TRUE);
+ if (is_cache)
+ cfile = g_strdup_printf ("%s_%s", evalue, data_type);
+ else
+ cfile = g_strdup_printf ("ldap-%s.%s", evalue, data_type);
+ g_free (evalue);
+
+ gchar *fname;
+ if (is_cache)
+ fname = g_build_path (G_DIR_SEPARATOR_S, g_get_user_cache_dir (),
+ "libgda", "ldap", cfile, NULL);
+ else
+ fname = g_build_path (G_DIR_SEPARATOR_S, g_get_user_data_dir (),
+ "libgda", cfile, NULL);
+
+ g_free (cfile);
+ return fname;
+}
+
+/*
+ * Open connection request
+ *
+ * In this function, the following _must_ be done:
+ * - check for the presence and validify of the parameters required to actually open a connection,
+ * using @params
+ * - open the real connection to the database using the parameters previously checked, create one or
+ * more GdaDataModel objects and declare them to the virtual connection with table names
+ * - open virtual (SQLite) connection
+ * - create a LdapConnectionData structure and associate it to @cnc
+ *
+ * Returns: TRUE if no error occurred, or FALSE otherwise (and an ERROR connection event must be added to @cnc)
+ */
+static gboolean
+gda_ldap_provider_open_connection (GdaServerProvider *provider, GdaConnection *cnc,
+ GdaQuarkList *params, GdaQuarkList *auth,
+ G_GNUC_UNUSED guint *task_id, GdaServerProviderAsyncCallback async_cb,
+ G_GNUC_UNUSED gpointer cb_data)
+{
+ g_return_val_if_fail (GDA_IS_LDAP_PROVIDER (provider), FALSE);
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+
+ /* Don't allow asynchronous connection opening for virtual providers */
+ if (async_cb) {
+ gda_connection_add_event_string (cnc, _("Provider does not support asynchronous connection open"));
+ return FALSE;
+ }
+
+ /* Check for connection parameters */
+ const gchar *base_dn;
+ const gchar *host;
+ const gchar *tmp;
+ const gchar *port;
+ const gchar *user = NULL;
+ const gchar *pwd = NULL;
+ gint rport;
+ gboolean use_ssl, use_cache;
+
+ base_dn = gda_quark_list_find (params, "DB_NAME");
+ if (!base_dn) {
+ gda_connection_add_event_string (cnc,
+ _("The connection string must contain the DB_NAME value"));
+ return FALSE;
+ }
+ host = gda_quark_list_find (params, "HOST");
+ if (!host)
+ host = "127.0.0.1";
+ port = gda_quark_list_find (params, "PORT");
+ tmp = gda_quark_list_find (params, "USE_SSL");
+ use_ssl = (tmp && ((*tmp == 't') || (*tmp == 'T'))) ? TRUE : FALSE;
+ tmp = gda_quark_list_find (params, "USE_CACHE");
+ use_cache = (!tmp || ((*tmp == 't') || (*tmp == 'T'))) ? TRUE : FALSE;
+ if (port && *port)
+ rport = atoi (port);
+ else {
+ if (use_ssl)
+ rport = LDAPS_PORT;
+ else
+ rport = LDAP_PORT;
+ }
+ user = gda_quark_list_find (auth, "USERNAME");
+ if (!user)
+ user = gda_quark_list_find (params, "USERNAME");
+ pwd = gda_quark_list_find (auth, "PASSWORD");
+ if (!pwd)
+ pwd = gda_quark_list_find (params, "PASSWORD");
+
+ /* open LDAP connection */
+ LdapConnectionData *cdata;
+ LDAP *ld;
+ int res;
+ gchar *url;
+ if (use_ssl)
+ url = g_strdup_printf ("ldaps://%s:%d", host, rport);
+ else
+ url = g_strdup_printf ("ldap://%s:%d", host, rport);
+ res = ldap_initialize (&ld, url);
+
+ if (res != LDAP_SUCCESS) {
+ gda_connection_add_event_string (cnc, ldap_err2string (res));
+ g_free (url);
+ return FALSE;
+ }
+
+ cdata = g_new0 (LdapConnectionData, 1);
+ cdata->handle = ld;
+ cdata->url = url;
+ cdata->base_dn = g_strdup (base_dn);
+ if (use_cache)
+ cdata->attributes_cache_file = compute_data_file_name (params, TRUE, "attrs");
+
+ /* set protocol version to 3 by default */
+ int version = LDAP_VERSION3;
+ res = ldap_set_option (cdata->handle, LDAP_OPT_PROTOCOL_VERSION, &version);
+ if (res != LDAP_SUCCESS) {
+ if (res == LDAP_PROTOCOL_ERROR) {
+ version = LDAP_VERSION2;
+ res = ldap_set_option (cdata->handle, LDAP_OPT_PROTOCOL_VERSION, &version);
+ }
+ if (res != LDAP_SUCCESS) {
+ gda_connection_add_event_string (cnc, ldap_err2string (res));
+ gda_ldap_free_cnc_data (cdata);
+ return FALSE;
+ }
+ }
+ int param = LDAP_OPT_ON;
+ res = ldap_set_option (cdata->handle, LDAP_OPT_RESTART, ¶m);
+
+#ifdef NO
+ if (use_ssl) {
+ /* Configuring SSL/TLS options:
+ * this is for texting purpose only, and should actually be done through LDAP's conf.
+ * files, see: man 5 ldap.conf
+ *
+ * For example ~/.ldaprc can contain:
+ * TLS_REQCERT demand
+ * TLS_CACERT /usr/share/ca-certificates/mozilla/Thawte_Premium_Server_CA.crt
+ *
+ * Note: if server certificate verification fails,
+ * the error message is: "Can't contact LDAP server"
+ */
+ int opt = LDAP_OPT_X_TLS_DEMAND;
+ res = ldap_set_option (cdata->handle, LDAP_OPT_X_TLS_REQUIRE_CERT, &opt);
+ if (res != LDAP_SUCCESS) {
+ gda_connection_add_event_string (cnc, ldap_err2string (res));
+ gda_ldap_free_cnc_data (cdata);
+ return FALSE;
+ }
+ }
+#endif
+
+ /* authentication */
+ struct berval cred;
+ memset (&cred, 0, sizeof (cred));
+ cred.bv_len = pwd && *pwd ? strlen (pwd) : 0;
+ cred.bv_val = pwd && *pwd ? (char *) pwd : NULL;
+ res = ldap_sasl_bind_s (ld, user, NULL, &cred, NULL, NULL, NULL);
+ if (res != LDAP_SUCCESS) {
+ gda_connection_add_event_string (cnc, ldap_err2string (res));
+ gda_ldap_free_cnc_data (cdata);
+ return FALSE;
+ }
+ if (pwd)
+ cdata->pass = g_strdup (pwd);
+ if (user)
+ cdata->user = g_strdup (user);
+
+ /* set startup file name */
+ gchar *fname;
+ fname = compute_data_file_name (params, FALSE, "start");
+ g_object_set ((GObject*) cnc, "startup-file", fname, NULL);
+ g_free (fname);
+
+ /* open virtual connection */
+ g_object_set_data ((GObject*) cnc, "__gda_connection_LDAP", (gpointer) 0x01);
+ gda_virtual_connection_internal_set_provider_data (GDA_VIRTUAL_CONNECTION (cnc),
+ cdata, (GDestroyNotify) gda_ldap_free_cnc_data);
+ if (! GDA_SERVER_PROVIDER_CLASS (parent_class)->open_connection (GDA_SERVER_PROVIDER (provider), cnc, params,
+ NULL, NULL, NULL, NULL)) {
+ gda_virtual_connection_internal_set_provider_data (GDA_VIRTUAL_CONNECTION (cnc), NULL, NULL);
+ gda_connection_add_event_string (cnc, _("Can't open virtual connection"));
+ gda_ldap_free_cnc_data (cdata);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/*
+ * Reopens a connection after the server has closed it (possibly because of a timeout)
+ *
+ * If it fails, then @cdata is left unchanged, otherwise it is modified to be useable again.
+ */
+gboolean
+gda_ldap_silently_rebind (LdapConnectionData *cdata)
+{
+ if (!cdata)
+ return FALSE;
+
+ /*g_print ("Trying to reconnect...\n");*/
+ LDAP *ld;
+ int res;
+ res = ldap_initialize (&ld, cdata->url);
+ if (res != LDAP_SUCCESS)
+ return FALSE;
+
+ /* set protocol version to 3 by default */
+ int version = LDAP_VERSION3;
+ res = ldap_set_option (ld, LDAP_OPT_PROTOCOL_VERSION, &version);
+ if (res != LDAP_SUCCESS) {
+ if (res == LDAP_PROTOCOL_ERROR) {
+ version = LDAP_VERSION2;
+ res = ldap_set_option (ld, LDAP_OPT_PROTOCOL_VERSION, &version);
+ }
+ if (res != LDAP_SUCCESS) {
+ ldap_unbind_ext (ld, NULL, NULL);
+ return FALSE;
+ }
+ }
+
+ /* authentication */
+ struct berval cred;
+ const gchar *pwd;
+ pwd = cdata->pass;
+ memset (&cred, 0, sizeof (cred));
+ cred.bv_len = pwd && *pwd ? strlen (pwd) : 0;
+ cred.bv_val = pwd && *pwd ? (char *) pwd : NULL;
+ res = ldap_sasl_bind_s (ld, cdata->user, NULL, &cred, NULL, NULL, NULL);
+ if (res != LDAP_SUCCESS) {
+ ldap_unbind_ext (ld, NULL, NULL);
+ return FALSE;
+ }
+
+ /* all ok */
+ if (cdata->handle) {
+ /* don't call ldap_unbind_ext() as it often crashed the application */
+ /*ldap_unbind_ext (cdata->handle, NULL, NULL);*/
+ }
+ cdata->handle = ld;
+
+ /*g_print ("Reconnected!\n");*/
+ return TRUE;
+}
+
+/*
+ * Server version request
+ */
+static const gchar *
+gda_ldap_provider_get_server_version (GdaServerProvider *provider, GdaConnection *cnc)
+{
+ LdapConnectionData *cdata;
+
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), NULL);
+ g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, NULL);
+
+ cdata = (LdapConnectionData*) gda_virtual_connection_internal_get_provider_data (GDA_VIRTUAL_CONNECTION (cnc));
+ if (!cdata)
+ return FALSE;
+
+ if (! cdata->server_version) {
+ /* FIXME: don't know how to get information about the LDAP server! */
+ }
+ return cdata->server_version;
+}
+
+/*
+ * Get database request
+ */
+static const gchar *
+gda_ldap_provider_get_database (GdaServerProvider *provider, GdaConnection *cnc)
+{
+ LdapConnectionData *cdata;
+
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), NULL);
+ g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, NULL);
+
+ cdata = (LdapConnectionData*) gda_virtual_connection_internal_get_provider_data (GDA_VIRTUAL_CONNECTION (cnc));
+ if (!cdata)
+ return NULL;
+ TO_IMPLEMENT;
+ return NULL;
+}
+
+/*
+ * Extra SQL
+ */
+typedef struct {
+ gchar *table_name;
+ gboolean other_args; /* set to %TRUE if any of the arguments below have been specified */
+ gchar *base_dn;
+ gchar *filter;
+ gchar *attributes;
+ GdaLdapSearchScope scope;
+} ExtraSqlCommand;
+static void extra_sql_command_free (ExtraSqlCommand *cmde);
+
+#define NOT_AN_EXTRA_SQL_COMMAND (ExtraSqlCommand*) 0x01
+#define SKIP_SPACES(x) for (; *(x) && (g_ascii_isspace (*(x)) || (*(x)=='\n')); (x)++)
+static ExtraSqlCommand *parse_extra_sql_command (gchar *cmd, const gchar *cmde_name,
+ GError **error);
+static GdaDataModel *table_parameters_describe (const gchar *base_dn, const gchar *filter,
+ const gchar *attributes,
+ GdaLdapSearchScope scope);
+static GObject *
+gda_ldap_provider_statement_execute (GdaServerProvider *provider, GdaConnection *cnc,
+ GdaStatement *stmt, GdaSet *params,
+ GdaStatementModelUsage model_usage,
+ GType *col_types, GdaSet **last_inserted_row,
+ guint *task_id, GdaServerProviderExecCallback async_cb,
+ gpointer cb_data, GError **error)
+{
+ if (async_cb) {
+ g_set_error (error, GDA_SERVER_PROVIDER_ERROR, GDA_SERVER_PROVIDER_METHOD_NON_IMPLEMENTED_ERROR,
+ "%s", _("Provider does not support asynchronous statement execution"));
+ return NULL;
+ }
+ gchar *sql;
+ sql = gda_statement_to_sql (stmt, params, NULL);
+ if (sql) {
+ /* parse SQL:
+ * CREATE LDAP TABLE <table name> WITH BASE='base_dn' FILTER='filter'
+ * ATTRIBUTES='attributes' SCOPE='scope'
+ */
+ gchar *ssql = sql;
+ SKIP_SPACES (ssql);
+ if (! g_ascii_strncasecmp (ssql, "CREATE", 6)) {
+ ExtraSqlCommand *cmde;
+ GError *lerror = NULL;
+ GObject *retval = NULL;
+ cmde = parse_extra_sql_command (ssql, "CREATE", &lerror);
+ if (cmde != NOT_AN_EXTRA_SQL_COMMAND) {
+ GdaConnectionEvent *event = NULL;
+ if (cmde) {
+ if (gda_ldap_connection_declare_table (GDA_LDAP_CONNECTION (cnc),
+ cmde->table_name, cmde->base_dn,
+ cmde->filter, cmde->attributes,
+ cmde->scope, &lerror))
+ retval = (GObject*) gda_set_new (NULL);
+ else {
+ event = gda_connection_point_available_event (cnc,
+ GDA_CONNECTION_EVENT_ERROR);
+ gda_connection_event_set_description (event, lerror && lerror->message ?
+ lerror->message : _("No detail"));
+ gda_connection_add_event (cnc, event);
+ g_propagate_error (error, lerror);
+ }
+ extra_sql_command_free (cmde);
+ }
+ else {
+ event = gda_connection_point_available_event (cnc,
+ GDA_CONNECTION_EVENT_ERROR);
+ gda_connection_event_set_description (event, lerror && lerror->message ?
+ lerror->message : _("No detail"));
+ gda_connection_add_event (cnc, event);
+ g_propagate_error (error, lerror);
+ }
+
+ gda_connection_internal_statement_executed (cnc, stmt, params, event);
+ g_free (sql);
+ return retval;
+ }
+ }
+
+ /* parse SQL:
+ * DROP LDAP TABLE <table name>
+ */
+ else if (! g_ascii_strncasecmp (ssql, "DROP", 4)) {
+ ExtraSqlCommand *cmde;
+ GError *lerror = NULL;
+ GObject *retval = NULL;
+ cmde = parse_extra_sql_command (ssql, "DROP", &lerror);
+ if ((cmde != NOT_AN_EXTRA_SQL_COMMAND) && !cmde->other_args) {
+ GdaConnectionEvent *event = NULL;
+ if (cmde) {
+ if (gda_ldap_connection_undeclare_table (GDA_LDAP_CONNECTION (cnc),
+ cmde->table_name, &lerror))
+ retval = (GObject*) gda_set_new (NULL);
+ else {
+ event = gda_connection_point_available_event (cnc,
+ GDA_CONNECTION_EVENT_ERROR);
+ gda_connection_event_set_description (event, lerror && lerror->message ?
+ lerror->message : _("No detail"));
+ gda_connection_add_event (cnc, event);
+ g_propagate_error (error, lerror);
+ }
+ extra_sql_command_free (cmde);
+ }
+ gda_connection_internal_statement_executed (cnc, stmt, params, event);
+ g_free (sql);
+ return retval;
+ }
+ }
+ /* parse SQL:
+ * ALTER LDAP TABLE <table name> [...]
+ * DESCRIBE LDAP TABLE <table name>
+ */
+ else if (! g_ascii_strncasecmp (ssql, "ALTER", 5) ||
+ ! g_ascii_strncasecmp (ssql, "DESCRIBE", 8)) {
+ ExtraSqlCommand *cmde;
+ GError *lerror = NULL;
+ GObject *retval = NULL;
+ gboolean alter;
+
+ alter = g_ascii_strncasecmp (ssql, "ALTER", 5) ? FALSE : TRUE;
+
+ cmde = parse_extra_sql_command (ssql, alter ? "ALTER" : "DESCRIBE", &lerror);
+ if ((cmde != NOT_AN_EXTRA_SQL_COMMAND) &&
+ (alter || (!alter && !cmde->other_args))) {
+ GdaConnectionEvent *event = NULL;
+ if (cmde) {
+ const gchar *base_dn, *filter, *attributes;
+ GdaLdapSearchScope scope;
+ if (gda_ldap_connection_describe_table (GDA_LDAP_CONNECTION (cnc),
+ cmde->table_name,
+ &base_dn, &filter,
+ &attributes, &scope, &lerror)) {
+ if (cmde->other_args) {
+ if (! cmde->base_dn && base_dn)
+ cmde->base_dn = g_strdup (base_dn);
+ if (! cmde->filter && filter)
+ cmde->filter = g_strdup (filter);
+ if (! cmde->attributes && attributes)
+ cmde->attributes = g_strdup (attributes);
+ if (! cmde->scope)
+ cmde->scope = scope;
+ if (gda_ldap_connection_undeclare_table (GDA_LDAP_CONNECTION (cnc),
+ cmde->table_name, &lerror) &&
+ gda_ldap_connection_declare_table (GDA_LDAP_CONNECTION (cnc),
+ cmde->table_name, cmde->base_dn,
+ cmde->filter, cmde->attributes,
+ cmde->scope, &lerror))
+ retval = (GObject*) gda_set_new (NULL);
+ }
+ else {
+ GdaDataModel *array;
+ array = table_parameters_describe (base_dn, filter,
+ attributes, scope);
+ retval = (GObject*) array;
+ }
+ }
+ if (!retval) {
+ event = gda_connection_point_available_event (cnc,
+ GDA_CONNECTION_EVENT_ERROR);
+ gda_connection_event_set_description (event, lerror && lerror->message ?
+ lerror->message : _("No detail"));
+ gda_connection_add_event (cnc, event);
+ g_propagate_error (error, lerror);
+ }
+ extra_sql_command_free (cmde);
+ }
+ gda_connection_internal_statement_executed (cnc, stmt, params, event);
+ g_free (sql);
+ return retval;
+ }
+ }
+ g_free (sql);
+ }
+ return GDA_SERVER_PROVIDER_CLASS (parent_class)->statement_execute (provider, cnc, stmt, params,
+ model_usage, col_types,
+ last_inserted_row, task_id,
+ async_cb, cb_data, error);
+}
+
+/*
+ * Free connection's specific data
+ */
+static void
+gda_ldap_free_cnc_data (LdapConnectionData *cdata)
+{
+ if (cdata->handle)
+ ldap_unbind_ext (cdata->handle, NULL, NULL);
+ if (cdata->attributes_hash)
+ g_hash_table_destroy (cdata->attributes_hash);
+ g_free (cdata->attributes_cache_file);
+ g_free (cdata->base_dn);
+ g_free (cdata->server_version);
+ g_free (cdata->url);
+ g_free (cdata->user);
+ g_free (cdata->pass);
+ g_free (cdata);
+}
+
+static const gchar *
+scope_to_string (GdaLdapSearchScope scope)
+{
+ switch (scope) {
+ case 0:
+ return _("Unknown");
+ case GDA_LDAP_SEARCH_BASE:
+ return "BASE";
+ case GDA_LDAP_SEARCH_ONELEVEL:
+ return "ONELEVEL";
+ case GDA_LDAP_SEARCH_SUBTREE:
+ return "SUBTREE";
+ default:
+ g_assert_not_reached();
+ return NULL;
+ }
+}
+
+static GdaDataModel *
+table_parameters_describe (const gchar *base_dn, const gchar *filter, const gchar *attributes,
+ GdaLdapSearchScope scope)
+{
+ GdaDataModel *array;
+ GValue *v1, *v2;
+ GList *list;
+ array = gda_data_model_array_new_with_g_types (2, G_TYPE_STRING,
+ G_TYPE_STRING);
+ gda_data_model_set_column_title (array, 0, _("Parameter"));
+ gda_data_model_set_column_title (array, 1, _("Value"));
+ g_value_set_string ((v1 = gda_value_new (G_TYPE_STRING)), "BASE");
+ g_value_set_string ((v2 = gda_value_new (G_TYPE_STRING)), base_dn);
+ list = g_list_append (NULL, v1);
+ list = g_list_append (list, v2);
+ gda_data_model_append_values (array, list, NULL);
+ g_list_free (list);
+ gda_value_free (v1);
+ gda_value_free (v2);
+
+ g_value_set_string ((v1 = gda_value_new (G_TYPE_STRING)), "FILTER");
+ g_value_set_string ((v2 = gda_value_new (G_TYPE_STRING)), filter);
+ list = g_list_append (NULL, v1);
+ list = g_list_append (list, v2);
+ gda_data_model_append_values (array, list, NULL);
+ g_list_free (list);
+ gda_value_free (v1);
+ gda_value_free (v2);
+
+ g_value_set_string ((v1 = gda_value_new (G_TYPE_STRING)), "ATTRIBUTES");
+ g_value_set_string ((v2 = gda_value_new (G_TYPE_STRING)), attributes);
+ list = g_list_append (NULL, v1);
+ list = g_list_append (list, v2);
+ gda_data_model_append_values (array, list, NULL);
+ g_list_free (list);
+ gda_value_free (v1);
+ gda_value_free (v2);
+
+ g_value_set_string ((v1 = gda_value_new (G_TYPE_STRING)), "SCOPE");
+ g_value_set_string ((v2 = gda_value_new (G_TYPE_STRING)), scope_to_string (scope));
+ list = g_list_append (NULL, v1);
+ list = g_list_append (list, v2);
+ gda_data_model_append_values (array, list, NULL);
+ g_list_free (list);
+ gda_value_free (v1);
+ gda_value_free (v2);
+ return array;
+}
+
+/*
+ * Extra commands parsing
+ */
+/*
+ * consumes @str for a singly quoted string
+ * Returns: a pointer to the next non analysed char
+ */
+static gchar *
+parse_string (gchar *str, gchar **out_part)
+{
+ gchar *ptr = str;
+ *out_part = NULL;
+
+ SKIP_SPACES (ptr);
+ if (!*ptr)
+ return NULL;
+ if (*ptr != '\'') {
+ if (!g_ascii_strncasecmp (ptr, "null", 4)) {
+ ptr += 4;
+ return ptr;
+ }
+ else
+ return NULL;
+ }
+ ptr++;
+ *out_part = ptr;
+ for (; *ptr && (*ptr != '\''); ptr++);
+ if (!*ptr)
+ return NULL;
+ *ptr = 0;
+ ptr++;
+ return ptr;
+}
+
+/*
+ * consumes @str for an identifier
+ * Returns: a pointer to the next non analysed char
+ */
+static gchar *
+parse_ident (gchar *str, gchar **out_part)
+{
+ gchar *ptr = str;
+ *out_part = NULL;
+
+ SKIP_SPACES (ptr);
+ *out_part = ptr;
+ for (; *ptr && (g_ascii_isalnum (*ptr) || (*ptr == '_')) ; ptr++);
+ if (ptr == *out_part) {
+ *out_part = NULL;
+ return NULL;
+ }
+ return ptr;
+}
+
+static void
+extra_sql_command_free (ExtraSqlCommand *cmde)
+{
+ g_free (cmde->table_name);
+ g_free (cmde->base_dn);
+ g_free (cmde->filter);
+ g_free (cmde->attributes);
+ g_free (cmde);
+}
+
+/*
+ * CREATE LDAP TABLE <table name> BASE='base_dn' FILTER='filter' ATTRIBUTES='attributes' SCOPE='scope'
+ * DROP LDAP TABLE <table name>
+ * ALTER LDAP TABLE <table name>
+ * ALTER LDAP TABLE <table name> BASE='base_dn' FILTER='filter' ATTRIBUTES='attributes' SCOPE='scope'
+ *
+ * Returns: a #ExtraSqlCommand pointer, or %NULL, or NOT_AN_EXTRA_SQL_COMMAND (if not "CREATE LDAP...")
+ */
+static ExtraSqlCommand *
+parse_extra_sql_command (gchar *cmd, const gchar *cmde_name, GError **error)
+{
+ ExtraSqlCommand *args;
+ gchar *ptr, *errptr, *part, *tmp;
+ args = g_new0 (ExtraSqlCommand, 1);
+ args->other_args = FALSE;
+
+ ptr = cmd + strlen (cmde_name);
+
+ /* make sure about complete command */
+ errptr = ptr;
+ if (! (ptr = parse_ident (ptr, &part)))
+ return NOT_AN_EXTRA_SQL_COMMAND;
+ if (!part || g_ascii_strncasecmp (part, "ldap", 4))
+ return NOT_AN_EXTRA_SQL_COMMAND;
+
+ errptr = ptr;
+ if (! (ptr = parse_ident (ptr, &part)))
+ goto onerror;
+ if (!part || g_ascii_strncasecmp (part, "table", 5))
+ goto onerror;
+
+ /* table name */
+ SKIP_SPACES (ptr);
+ errptr = ptr;
+ if (! (ptr = parse_ident (ptr, &part)))
+ goto onerror;
+ tmp = g_strndup (part, ptr-part);
+ args->table_name = g_ascii_strdown (tmp, -1);
+ g_free (tmp);
+
+ /* key=value arguments */
+ while (TRUE) {
+ errptr = ptr;
+ SKIP_SPACES (ptr);
+ if (! (ptr = parse_ident (ptr, &part))) {
+ ptr = errptr;
+ break;
+ }
+ if (part) {
+ gchar **where = NULL;
+ if (!g_ascii_strncasecmp (part, "base", 4))
+ where = &(args->base_dn);
+ else if (!g_ascii_strncasecmp (part, "filter", 6))
+ where = &(args->filter);
+ else if (!g_ascii_strncasecmp (part, "attributes", 10))
+ where = &(args->attributes);
+ else if (!g_ascii_strncasecmp (part, "scope", 5))
+ where = NULL;
+ else
+ goto onerror;
+
+ /* = */
+ errptr = ptr;
+ SKIP_SPACES (ptr);
+ if (*ptr != '=')
+ goto onerror;
+ ptr++;
+
+ /* value */
+ errptr = ptr;
+ SKIP_SPACES (ptr);
+ if (! (ptr = parse_string (ptr, &part)))
+ goto onerror;
+ if (part) {
+ if (where)
+ *where = g_strdup (part);
+ else {
+ if (!g_ascii_strcasecmp (part, "base"))
+ args->scope = GDA_LDAP_SEARCH_BASE;
+ else if (!g_ascii_strcasecmp (part, "onelevel"))
+ args->scope = GDA_LDAP_SEARCH_ONELEVEL;
+ else if (!g_ascii_strcasecmp (part, "subtree"))
+ args->scope = GDA_LDAP_SEARCH_SUBTREE;
+ else
+ goto onerror;
+ }
+ args->other_args = TRUE;
+ }
+ else
+ goto onerror;
+ }
+ else
+ break;
+ }
+
+ /* end */
+ SKIP_SPACES (ptr);
+ if (*ptr && (*ptr != ';'))
+ goto onerror;
+#ifdef GDA_DEBUG_NO
+ g_print ("TABLE=>%s, BASE=>%s, FILTER=>%s, ATTRIBUTES=>%s, SCOPE=>%d\n", args->table_name,
+ args->base_dn, args->filter,
+ args->attributes, args->scope);
+#endif
+
+ return args;
+
+ onerror:
+ SKIP_SPACES (errptr);
+ g_set_error (error, GDA_SQL_PARSER_ERROR, GDA_SQL_PARSER_SYNTAX_ERROR,
+ _("near \"%s\": syntax error"), errptr);
+ extra_sql_command_free (args);
+ return NULL;
+}
diff --git a/providers/ldap/gda-ldap-provider.h b/providers/ldap/gda-ldap-provider.h
new file mode 100644
index 0000000..ad15222
--- /dev/null
+++ b/providers/ldap/gda-ldap-provider.h
@@ -0,0 +1,50 @@
+/* GDA Ldap provider
+ * Copyright (C) 2008 The GNOME Foundation
+ *
+ * AUTHORS:
+ * TO_ADD: your name and email
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __GDA_LDAP_PROVIDER_H__
+#define __GDA_LDAP_PROVIDER_H__
+
+#include <virtual/gda-vprovider-data-model.h>
+
+#define GDA_TYPE_LDAP_PROVIDER (gda_ldap_provider_get_type())
+#define GDA_LDAP_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, GDA_TYPE_LDAP_PROVIDER, GdaLdapProvider))
+#define GDA_LDAP_PROVIDER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, GDA_TYPE_LDAP_PROVIDER, GdaLdapProviderClass))
+#define GDA_IS_LDAP_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, GDA_TYPE_LDAP_PROVIDER))
+#define GDA_IS_LDAP_PROVIDER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDA_TYPE_LDAP_PROVIDER))
+
+typedef struct _GdaLdapProvider GdaLdapProvider;
+typedef struct _GdaLdapProviderClass GdaLdapProviderClass;
+
+struct _GdaLdapProvider {
+ GdaVproviderDataModel provider;
+};
+
+struct _GdaLdapProviderClass {
+ GdaVproviderDataModelClass parent_class;
+};
+
+G_BEGIN_DECLS
+
+GType gda_ldap_provider_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif
diff --git a/providers/ldap/gda-ldap-util.c b/providers/ldap/gda-ldap-util.c
new file mode 100644
index 0000000..fbf5f8d
--- /dev/null
+++ b/providers/ldap/gda-ldap-util.c
@@ -0,0 +1,1410 @@
+/*
+ * Copyright (C) 2011 The GNOME Foundation
+ *
+ * AUTHORS:
+ * Vivien Malerba <malerba gnome-db org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <glib-object.h>
+#include <libgda/gda-value.h>
+#include <glib/gi18n-lib.h>
+#include "gda-ldap.h"
+#include "gda-ldap-util.h"
+#include <sqlite/virtual/gda-ldap-connection.h>
+#include <gda-util.h>
+
+static void
+ldap_attribute_free (LdapAttribute *lat)
+{
+ g_free (lat->name);
+ g_free (lat);
+}
+
+static void
+ldap_class_free (GdaLdapClass *lcl)
+{
+ g_free (lcl->oid);
+ g_strfreev (lcl->names);
+ g_free (lcl->description);
+
+ if (lcl->req_attributes)
+ g_strfreev (lcl->req_attributes);
+
+ if (lcl->opt_attributes)
+ g_strfreev (lcl->opt_attributes);
+ g_slist_free (lcl->parents);
+ g_slist_free (lcl->children);
+ g_free (lcl);
+}
+
+/*
+ * Data copied from GQ's sources and transformed,
+ * see ftp://ftp.rfc-editor.org/in-notes/rfc2252.txt
+ */
+static LdapAttrType ldap_types [] = {
+ { "1.3.6.1.4.1.1466.115.121.1.1",
+ "ACI Item",
+ -1 /*GDA_TYPE_BINARY*/
+ },
+ { "1.3.6.1.4.1.1466.115.121.1.2",
+ "Access Point",
+ G_TYPE_STRING
+ },
+ { "1.3.6.1.4.1.1466.115.121.1.3",
+ "Attribute Type Description",
+ G_TYPE_STRING
+ },
+ { "1.3.6.1.4.1.1466.115.121.1.4",
+ "Audio",
+ -1 /*GDA_TYPE_BINARY*/
+ },
+ { "1.3.6.1.4.1.1466.115.121.1.5",
+ "Binary",
+ -1 /*GDA_TYPE_BINARY*/
+ },
+ { "1.3.6.1.4.1.1466.115.121.1.6",
+ "Bit String",
+ G_TYPE_STRING
+ },
+ { "1.3.6.1.4.1.1466.115.121.1.7",
+ "Boolean",
+ G_TYPE_BOOLEAN
+ },
+ { "1.3.6.1.4.1.1466.115.121.1.8",
+ "Certificate",
+ G_TYPE_STRING
+ },
+ { "1.3.6.1.4.1.1466.115.121.1.9",
+ "Certificate List",
+ G_TYPE_STRING
+ },
+ { "1.3.6.1.4.1.1466.115.121.1.10",
+ "Certificate Pair",
+ -1 /*GDA_TYPE_BINARY*/,
+ },
+ { "1.3.6.1.4.1.1466.115.121.1.11",
+ "Country String",
+ G_TYPE_STRING
+ },
+ { "1.3.6.1.4.1.1466.115.121.1.12",
+ "DN",
+ G_TYPE_STRING
+ },
+ { "1.3.6.1.4.1.1466.115.121.1.13",
+ "Data Quality Syntax",
+ G_TYPE_STRING
+ },
+ { "1.3.6.1.4.1.1466.115.121.1.14",
+ "Delivery Method",
+ G_TYPE_STRING
+ },
+ { "1.3.6.1.4.1.1466.115.121.1.15",
+ "Directory String",
+ G_TYPE_STRING
+ },
+ { "1.3.6.1.4.1.1466.115.121.1.16",
+ "DIT Content Rule Description",
+ G_TYPE_STRING
+ },
+ { "1.3.6.1.4.1.1466.115.121.1.17",
+ "DIT Structure Rule Description",
+ G_TYPE_STRING
+ },
+ { "1.3.6.1.4.1.1466.115.121.1.18",
+ "DL Submit Permission",
+ G_TYPE_STRING
+ },
+ { "1.3.6.1.4.1.1466.115.121.1.19",
+ "DSA Quality Syntax",
+ G_TYPE_STRING
+ },
+ { "1.3.6.1.4.1.1466.115.121.1.20",
+ "DSE Type",
+ G_TYPE_STRING
+ },
+ { "1.3.6.1.4.1.1466.115.121.1.21",
+ "Enhanced Guide",
+ G_TYPE_STRING
+ },
+ { "1.3.6.1.4.1.1466.115.121.1.22",
+ "Facsimile Telephone Number",
+ G_TYPE_STRING
+ },
+ { "1.3.6.1.4.1.1466.115.121.1.23",
+ "Fax",
+ -1 /*GDA_TYPE_BINARY*/
+ },
+ { "1.3.6.1.4.1.1466.115.121.1.24",
+ "Generalized Time",
+ -4 /*GDA_TYPE_TIMESTAMP*/
+ },
+ { "1.3.6.1.4.1.1466.115.121.1.25",
+ "Guide",
+ G_TYPE_STRING
+ },
+ { "1.3.6.1.4.1.1466.115.121.1.26",
+ "IA5 String",
+ G_TYPE_STRING /* as ASCII */
+ },
+ { "1.3.6.1.4.1.1466.115.121.1.27",
+ "INTEGER",
+ G_TYPE_INT
+ },
+ { "1.3.6.1.4.1.1466.115.121.1.28",
+ "JPEG",
+ -1 /*GDA_TYPE_BINARY*/
+ },
+ { "1.3.6.1.4.1.1466.115.121.1.29",
+ "Master And Shadow Access Points",
+ G_TYPE_STRING
+ },
+ { "1.3.6.1.4.1.1466.115.121.1.30",
+ "Matching Rule Description",
+ G_TYPE_STRING
+ },
+ { "1.3.6.1.4.1.1466.115.121.1.31",
+ "Matching Rule Use Description",
+ G_TYPE_STRING
+ },
+ { "1.3.6.1.4.1.1466.115.121.1.32",
+ "Mail Preference",
+ G_TYPE_STRING
+ },
+ { "1.3.6.1.4.1.1466.115.121.1.33",
+ "MHS OR Address",
+ G_TYPE_STRING
+ },
+ { "1.3.6.1.4.1.1466.115.121.1.34",
+ "Name And Optional UID",
+ G_TYPE_STRING
+ },
+ { "1.3.6.1.4.1.1466.115.121.1.35",
+ "Name Form Description",
+ G_TYPE_STRING
+ },
+ { "1.3.6.1.4.1.1466.115.121.1.36",
+ "Numeric String",
+ -3 /*GDA_TYPE_NUMERIC*/
+ },
+ { "1.3.6.1.4.1.1466.115.121.1.37",
+ "Object Class Description",
+ G_TYPE_STRING
+ },
+ { "1.3.6.1.4.1.1466.115.121.1.38",
+ "OID",
+ G_TYPE_STRING
+ },
+ { "1.3.6.1.4.1.1466.115.121.1.39",
+ "Other Mailbox",
+ G_TYPE_STRING
+ },
+ { "1.3.6.1.4.1.1466.115.121.1.40",
+ "Octet String",
+ -1 /*GDA_TYPE_BINARY*/
+ },
+ { "1.3.6.1.4.1.1466.115.121.1.41",
+ "Postal Address",
+ G_TYPE_STRING
+ },
+ { "1.3.6.1.4.1.1466.115.121.1.42",
+ "Protocol Information",
+ G_TYPE_STRING
+ },
+ { "1.3.6.1.4.1.1466.115.121.1.43",
+ "Presentation Address",
+ G_TYPE_STRING
+ },
+ { "1.3.6.1.4.1.1466.115.121.1.44",
+ "Printable String",
+ G_TYPE_STRING
+ },
+ { "1.3.6.1.4.1.1466.115.121.1.45",
+ "Subtree Specification",
+ G_TYPE_STRING
+ },
+ { "1.3.6.1.4.1.1466.115.121.1.46",
+ "Supplier Information",
+ G_TYPE_STRING
+ },
+ { "1.3.6.1.4.1.1466.115.121.1.47",
+ "Supplier Or Consumer",
+ G_TYPE_STRING
+ },
+ { "1.3.6.1.4.1.1466.115.121.1.48",
+ "Supplier And Consumer",
+ G_TYPE_STRING
+ },
+ { "1.3.6.1.4.1.1466.115.121.1.49",
+ "Supported Algorithm",
+ G_TYPE_STRING
+ },
+ { "1.3.6.1.4.1.1466.115.121.1.50",
+ "Telephone Number",
+ G_TYPE_STRING
+ },
+ { "1.3.6.1.4.1.1466.115.121.1.51",
+ "Teletex Terminal Identifier",
+ G_TYPE_STRING
+ },
+ { "1.3.6.1.4.1.1466.115.121.1.52",
+ "Telex Number",
+ G_TYPE_STRING
+ },
+ { "1.3.6.1.4.1.1466.115.121.1.53",
+ "UTC Time",
+ -2 /*GDA_TYPE_TIME*/
+ },
+ { "1.3.6.1.4.1.1466.115.121.1.54",
+ "LDAP Syntax Description",
+ G_TYPE_STRING
+ },
+ { "1.3.6.1.4.1.1466.115.121.1.55",
+ "Modify Rights",
+ G_TYPE_STRING
+ },
+ { "1.3.6.1.4.1.1466.115.121.1.56",
+ "LDAP Schema Definition",
+ G_TYPE_STRING
+ },
+ { "1.3.6.1.4.1.1466.115.121.1.57",
+ "LDAP Schema Description",
+ G_TYPE_STRING
+ },
+ { "1.3.6.1.4.1.1466.115.121.1.58",
+ "Substring Assertion",
+ G_TYPE_STRING
+ }
+};
+
+LdapAttrType unknown_type = {
+ "", "Unknown type", G_TYPE_STRING
+};
+
+/**
+ * gda_ldap_get_type_info:
+ *
+ * Returns: the #LdapAttrType associated to @oid, NEVER NULL
+ */
+LdapAttrType *
+gda_ldap_get_type_info (const gchar *oid)
+{
+ static GHashTable *hash = NULL;
+ LdapAttrType *retval = NULL;
+ if (hash) {
+ if (oid)
+ retval = g_hash_table_lookup (hash, oid);
+ }
+ else {
+ hash = g_hash_table_new (g_str_hash, g_str_equal);
+ gint i, nb;
+ nb = sizeof (ldap_types) / sizeof (LdapAttrType);
+ for (i = 0; i < nb; i++) {
+ LdapAttrType *type;
+ type = & (ldap_types[i]);
+ if (type->gtype == -1)
+ type->gtype = GDA_TYPE_BINARY;
+ else if (type->gtype == -2)
+ type->gtype = GDA_TYPE_TIME;
+ else if (type->gtype == -3)
+ type->gtype = GDA_TYPE_NUMERIC;
+ else if (type->gtype == -4)
+ type->gtype = GDA_TYPE_TIMESTAMP;
+ g_hash_table_insert (hash, type->oid, type);
+ }
+ if (oid)
+ retval = g_hash_table_lookup (hash, oid);
+ }
+ return retval ? retval : &unknown_type;
+}
+
+/**
+ * gda_ldap_get_attr_info:
+ *
+ * Returns: the #LdapAttribute for @attribute, or %NULL
+ */
+LdapAttribute *
+gda_ldap_get_attr_info (LdapConnectionData *cdata, const gchar *attribute)
+{
+ LdapAttribute *retval = NULL;
+ if (! attribute || !cdata)
+ return NULL;
+
+ if (cdata->attributes_hash)
+ return g_hash_table_lookup (cdata->attributes_hash, attribute);
+
+ /* initialize known types */
+ cdata->attributes_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
+ NULL,
+ (GDestroyNotify) ldap_attribute_free);
+
+
+ if (cdata->attributes_cache_file) {
+ /* try to load from cache file, which must contain one line per attribute:
+ * <syntax oid>,0|1,<attribute name>
+ */
+ gchar *data;
+ if (g_file_get_contents (cdata->attributes_cache_file, &data, NULL, NULL)) {
+ gchar *start, *ptr;
+ gchar **array;
+ start = data;
+ while (1) {
+ gboolean done = FALSE;
+ for (ptr = start; *ptr && (*ptr != '\n'); ptr++);
+ if (*ptr == '\n')
+ *ptr = 0;
+ else
+ done = TRUE;
+
+ if (*start && (*start != '#')) {
+ array = g_strsplit (start, ",", 3);
+ if (array[0] && array[1] && array[2]) {
+ LdapAttribute *lat;
+ lat = g_new (LdapAttribute, 1);
+ lat->name = g_strdup (array[2]);
+ lat->type = gda_ldap_get_type_info (array[0]);
+ lat->single_value = (*array[1] == '0' ? FALSE : TRUE);
+ g_hash_table_insert (cdata->attributes_hash,
+ lat->name, lat);
+ /*g_print ("CACHE ADDED [%s][%p][%d] for OID %s\n",
+ lat->name, lat->type, lat->single_value,
+ array[0]);*/
+ }
+ g_strfreev (array);
+ }
+ if (done)
+ break;
+ else
+ start = ptr+1;
+ }
+ g_free (data);
+ return g_hash_table_lookup (cdata->attributes_hash, attribute);
+ }
+ }
+
+ GString *string = NULL;
+ LDAPMessage *msg, *entry;
+ int res;
+ gchar *subschema = NULL;
+
+ char *subschemasubentry[] = {"subschemaSubentry", NULL};
+ char *schema_attrs[] = {"attributeTypes", NULL};
+
+ /* look for subschema */
+ res = ldap_search_ext_s (cdata->handle, "", LDAP_SCOPE_BASE,
+ "(objectclass=*)",
+ subschemasubentry, 0,
+ NULL, NULL, NULL, 0,
+ &msg);
+ if (res != LDAP_SUCCESS)
+ return NULL;
+
+ if ((entry = ldap_first_entry (cdata->handle, msg))) {
+ char *attr;
+ BerElement *ber;
+ if ((attr = ldap_first_attribute (cdata->handle, entry, &ber))) {
+ BerValue **bvals;
+ if ((bvals = ldap_get_values_len (cdata->handle, entry, attr))) {
+ subschema = g_strdup (bvals[0]->bv_val);
+ ldap_value_free_len (bvals);
+ }
+ ldap_memfree (attr);
+ }
+ if (ber)
+ ber_free (ber, 0);
+ }
+ ldap_msgfree (msg);
+
+ if (! subschema)
+ return NULL;
+
+ /* look for attributeTypes */
+ res = ldap_search_ext_s (cdata->handle, subschema, LDAP_SCOPE_BASE,
+ "(objectclass=*)",
+ schema_attrs, 0,
+ NULL, NULL, NULL, 0,
+ &msg);
+ g_free (subschema);
+ if (res != LDAP_SUCCESS)
+ return NULL;
+
+ if (cdata->attributes_cache_file)
+ string = g_string_new ("# Cache file. This file can safely be removed, in this case\n"
+ "# it will be automatically recreated.\n"
+ "# DO NOT MODIFY\n");
+ for (entry = ldap_first_entry (cdata->handle, msg);
+ entry;
+ entry = ldap_next_entry (cdata->handle, msg)) {
+ char *attr;
+ BerElement *ber;
+ for (attr = ldap_first_attribute (cdata->handle, msg, &ber);
+ attr;
+ attr = ldap_next_attribute (cdata->handle, msg, ber)) {
+ if (strcasecmp(attr, "attributeTypes")) {
+ ldap_memfree (attr);
+ continue;
+ }
+
+ BerValue **bvals;
+ bvals = ldap_get_values_len (cdata->handle, entry, attr);
+ if (bvals) {
+ gint i;
+ for (i = 0; bvals[i]; i++) {
+ LDAPAttributeType *at;
+ const char *errp;
+ int retcode;
+ at = ldap_str2attributetype (bvals[i]->bv_val, &retcode,
+ &errp,
+ LDAP_SCHEMA_ALLOW_ALL);
+ if (at && at->at_names && at->at_syntax_oid &&
+ at->at_names[0] && *(at->at_names[0])) {
+ LdapAttribute *lat;
+ lat = g_new (LdapAttribute, 1);
+ lat->name = g_strdup (at->at_names [0]);
+ lat->type = gda_ldap_get_type_info (at->at_syntax_oid);
+ lat->single_value = (at->at_single_value == 0 ? FALSE : TRUE);
+ g_hash_table_insert (cdata->attributes_hash,
+ lat->name, lat);
+ /*g_print ("ADDED [%s][%p][%d] for OID %s\n",
+ lat->name, lat->type, lat->single_value,
+ at->at_syntax_oid);*/
+ if (string)
+ g_string_append_printf (string, "%s,%d,%s\n",
+ at->at_syntax_oid,
+ lat->single_value,
+ lat->name);
+
+ }
+ if (at)
+ ldap_memfree (at);
+ }
+ ldap_value_free_len (bvals);
+ }
+
+ ldap_memfree (attr);
+ }
+ if (ber)
+ ber_free (ber, 0);
+ }
+ ldap_msgfree (msg);
+
+ if (string) {
+ if (! g_file_set_contents (cdata->attributes_cache_file, string->str, -1, NULL)) {
+ gchar *dirname;
+ dirname = g_path_get_dirname (cdata->attributes_cache_file);
+ g_mkdir_with_parents (dirname, 0700);
+ g_free (dirname);
+ g_file_set_contents (cdata->attributes_cache_file, string->str, -1, NULL);
+ }
+ g_string_free (string, TRUE);
+ }
+
+ retval = g_hash_table_lookup (cdata->attributes_hash, attribute);
+ return retval;
+}
+
+/*
+ * Classes
+ */
+static gchar **make_array_from_strv (char **values, guint *out_size);
+static void classes_h_func (GdaLdapClass *lcl, gchar **supclasses, LdapConnectionData *cdata);
+static gint classes_sort (GdaLdapClass *lcl1, GdaLdapClass *lcl2);
+
+/**
+ * gdaprov_ldap_get_class_info:
+ * @cdata:
+ * @class:
+ *
+ * Returns: the #GdaLdapClass for @classname, or %NULL
+ */
+GdaLdapClass *
+gdaprov_ldap_get_class_info (GdaLdapConnection *cnc, const gchar *classname)
+{
+ GdaLdapClass *retval = NULL;
+ LdapConnectionData *cdata;
+ g_return_val_if_fail (GDA_IS_LDAP_CONNECTION (cnc), NULL);
+ g_return_val_if_fail (classname, NULL);
+
+ cdata = (LdapConnectionData*) gda_virtual_connection_internal_get_provider_data (GDA_VIRTUAL_CONNECTION (cnc));
+ if (!cdata)
+ return NULL;
+
+ if (cdata->classes_hash)
+ return g_hash_table_lookup (cdata->classes_hash, classname);
+
+ /* initialize known classes */
+ cdata->classes_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
+ NULL,
+ (GDestroyNotify) ldap_class_free);
+
+ LDAPMessage *msg, *entry;
+ int res;
+ gchar *subschema = NULL;
+
+ char *subschemasubentry[] = {"subschemaSubentry", NULL};
+ char *schema_attrs[] = {"objectClasses", NULL};
+
+ /* look for subschema */
+ res = ldap_search_ext_s (cdata->handle, "", LDAP_SCOPE_BASE,
+ "(objectclass=*)",
+ subschemasubentry, 0,
+ NULL, NULL, NULL, 0,
+ &msg);
+ if (res != LDAP_SUCCESS)
+ return NULL;
+
+ if ((entry = ldap_first_entry (cdata->handle, msg))) {
+ char *attr;
+ BerElement *ber;
+ if ((attr = ldap_first_attribute (cdata->handle, entry, &ber))) {
+ BerValue **bvals;
+ if ((bvals = ldap_get_values_len (cdata->handle, entry, attr))) {
+ subschema = g_strdup (bvals[0]->bv_val);
+ ldap_value_free_len (bvals);
+ }
+ ldap_memfree (attr);
+ }
+ if (ber)
+ ber_free (ber, 0);
+ }
+ ldap_msgfree (msg);
+
+ if (! subschema)
+ return NULL;
+
+ /* look for attributeTypes */
+ res = ldap_search_ext_s (cdata->handle, subschema, LDAP_SCOPE_BASE,
+ "(objectclass=*)",
+ schema_attrs, 0,
+ NULL, NULL, NULL, 0,
+ &msg);
+ g_free (subschema);
+ if (res != LDAP_SUCCESS)
+ return NULL;
+
+ GHashTable *h_refs;
+ h_refs = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) g_strfreev);
+ for (entry = ldap_first_entry (cdata->handle, msg);
+ entry;
+ entry = ldap_next_entry (cdata->handle, msg)) {
+ char *attr;
+ BerElement *ber;
+ for (attr = ldap_first_attribute (cdata->handle, msg, &ber);
+ attr;
+ attr = ldap_next_attribute (cdata->handle, msg, ber)) {
+ if (strcasecmp(attr, "objectClasses")) {
+ ldap_memfree (attr);
+ continue;
+ }
+
+ BerValue **bvals;
+ bvals = ldap_get_values_len (cdata->handle, entry, attr);
+ if (bvals) {
+ gint i;
+ for (i = 0; bvals[i]; i++) {
+ LDAPObjectClass *oc;
+ const char *errp;
+ int retcode;
+ oc = ldap_str2objectclass (bvals[i]->bv_val, &retcode,
+ &errp,
+ LDAP_SCHEMA_ALLOW_ALL);
+ if (oc && oc->oc_oid && oc->oc_names && oc->oc_names[0]) {
+ GdaLdapClass *lcl;
+ guint k;
+ lcl = g_new0 (GdaLdapClass, 1);
+ lcl->oid = g_strdup (oc->oc_oid);
+//#define CLASS_DEBUG
+#ifdef CLASS_DEBUG
+ g_print ("FOUND CLASS\n");
+#endif
+ lcl->names = make_array_from_strv (oc->oc_names,
+ &(lcl->nb_names));
+ for (k = 0; lcl->names[k]; k++) {
+#ifdef CLASS_DEBUG
+ g_print (" oc_names[%d] = %s\n",
+ k, lcl->names[k]);
+#endif
+ g_hash_table_insert (cdata->classes_hash,
+ lcl->names[k],
+ lcl);
+ }
+ if (oc->oc_desc) {
+#ifdef CLASS_DEBUG
+ g_print (" oc_desc = %s\n", oc->oc_desc);
+#endif
+ lcl->description = g_strdup (oc->oc_desc);
+ }
+#ifdef CLASS_DEBUG
+ g_print (" oc_kind = %d\n", oc->oc_kind);
+#endif
+ switch (oc->oc_kind) {
+ case 0:
+ lcl->kind = GDA_LDAP_CLASS_KIND_ABSTRACT;
+ break;
+ case 1:
+ lcl->kind = GDA_LDAP_CLASS_KIND_STRUTURAL;
+ break;
+ case 2:
+ lcl->kind = GDA_LDAP_CLASS_KIND_AUXILIARY;
+ break;
+ default:
+ lcl->kind = GDA_LDAP_CLASS_KIND_UNKNOWN;
+ break;
+ }
+ lcl->obsolete = oc->oc_obsolete;
+#ifdef CLASS_DEBUG
+ g_print (" oc_obsolete = %d\n", oc->oc_obsolete);
+
+#endif
+ gchar **refs;
+ refs = make_array_from_strv (oc->oc_sup_oids, NULL);
+ if (refs)
+ g_hash_table_insert (h_refs, lcl, refs);
+ else
+ cdata->top_classes = g_slist_insert_sorted (cdata->top_classes,
+ lcl, (GCompareFunc) classes_sort);
+#ifdef CLASS_DEBUG
+ for (k = 0; oc->oc_sup_oids && oc->oc_sup_oids[k]; k++)
+ g_print (" oc_sup_oids[0] = %s\n",
+ oc->oc_sup_oids[k]);
+#endif
+
+ lcl->req_attributes =
+ make_array_from_strv (oc->oc_at_oids_must,
+ &(lcl->nb_req_attributes));
+#ifdef CLASS_DEBUG
+ for (k = 0; oc->oc_at_oids_must && oc->oc_at_oids_must[k]; k++)
+ g_print (" oc_at_oids_must[0] = %s\n",
+ oc->oc_at_oids_must[k]);
+#endif
+ lcl->opt_attributes =
+ make_array_from_strv (oc->oc_at_oids_may,
+ &(lcl->nb_opt_attributes));
+#ifdef CLASS_DEBUG
+ for (k = 0; oc->oc_at_oids_may && oc->oc_at_oids_may[k]; k++)
+ g_print (" oc_at_oids_may[0] = %s\n",
+ oc->oc_at_oids_may[k]);
+#endif
+
+ }
+ if (oc)
+ ldap_memfree (oc);
+ }
+ ldap_value_free_len (bvals);
+ }
+
+ ldap_memfree (attr);
+ }
+ if (ber)
+ ber_free (ber, 0);
+ }
+ ldap_msgfree (msg);
+
+ /* create hierarchy */
+ g_hash_table_foreach (h_refs, (GHFunc) classes_h_func, cdata);
+ g_hash_table_destroy (h_refs);
+
+ retval = g_hash_table_lookup (cdata->classes_hash, classname);
+ return retval;
+}
+
+static gchar **
+make_array_from_strv (char **values, guint *out_size)
+{
+ if (out_size)
+ *out_size = 0;
+ if (!values)
+ return NULL;
+ GArray *array;
+ gint i;
+ array = g_array_new (TRUE, FALSE, sizeof (gchar*));
+ for (i = 0; values[i]; i++) {
+ gchar *tmp;
+ tmp = g_strdup (values [i]);
+ g_array_append_val (array, tmp);
+ }
+ if (out_size)
+ *out_size = array->len;
+
+ return (gchar**) g_array_free (array, FALSE);
+}
+
+static gint
+classes_sort (GdaLdapClass *lcl1, GdaLdapClass *lcl2)
+{
+ return g_ascii_strcasecmp (lcl1->names[0], lcl2->names[0]);
+}
+
+static void
+classes_h_func (GdaLdapClass *lcl, gchar **supclasses, LdapConnectionData *cdata)
+{
+ gint i;
+ for (i = 0; supclasses [i]; i++) {
+ GdaLdapClass *parent;
+ gchar *clname = supclasses [i];
+#ifdef CLASS_DEBUG
+ g_print ("class [%s] inherits [%s]\n", lcl->names[0], clname);
+#endif
+ parent = g_hash_table_lookup (cdata->classes_hash, clname);
+ if (!parent)
+ continue;
+ lcl->parents = g_slist_insert_sorted (lcl->parents, parent, (GCompareFunc) classes_sort);
+ parent->children = g_slist_insert_sorted (parent->children, lcl, (GCompareFunc) classes_sort);
+ }
+ if ((i == 0) && !g_slist_find (cdata->top_classes, lcl))
+ cdata->top_classes = g_slist_insert_sorted (cdata->top_classes, lcl, (GCompareFunc) classes_sort);
+}
+
+/*
+ * _gda_ldap_get_top_classes
+ */
+const GSList *
+gdaprov_ldap_get_top_classes (GdaLdapConnection *cnc)
+{
+ LdapConnectionData *cdata;
+ g_return_val_if_fail (GDA_IS_LDAP_CONNECTION (cnc), NULL);
+
+ cdata = (LdapConnectionData*) gda_virtual_connection_internal_get_provider_data (GDA_VIRTUAL_CONNECTION (cnc));
+ if (!cdata)
+ return NULL;
+ if (! cdata->classes_hash) {
+ /* force classes init */
+ GdaLdapClass *lcl;
+ lcl = gdaprov_ldap_get_class_info (cnc, "top");
+ }
+ return cdata->top_classes;
+}
+
+/*
+ * gda_ldap_get_g_type
+ *
+ * Compute the GType either from a specified GType of from the attribute name (giving precedence
+ * to the specified GType if any)
+ */
+GType
+gda_ldap_get_g_type (LdapConnectionData *cdata, const gchar *attribute_name, const gchar *specified_gtype)
+{
+ GType coltype = GDA_TYPE_NULL;
+ if (specified_gtype)
+ coltype = gda_g_type_from_string (specified_gtype);
+ if (coltype == GDA_TYPE_NULL) {
+ LdapAttribute *lat;
+ lat = gda_ldap_get_attr_info (cdata, attribute_name);
+ if (lat)
+ coltype = lat->type->gtype;
+ }
+ if (coltype == GDA_TYPE_NULL)
+ coltype = G_TYPE_STRING;
+ return coltype;
+}
+
+/*
+ * gda_ldap_attr_value_to_g_value:
+ * Converts a #BerValue to a new #GValue
+ *
+ * Returns: a new #GValue, or %NULL on error
+ */
+GValue *
+gda_ldap_attr_value_to_g_value (LdapConnectionData *cdata, GType type, BerValue *bv)
+{
+ GValue *value = NULL;
+ if ((type == GDA_TYPE_TIMESTAMP) ||
+ (type == G_TYPE_DATE)) {
+ /* see ftp://ftp.rfc-editor.org/in-notes/rfc4517.txt,
+ * section 3.3.13: Generalized Time
+ */
+ GTimeVal tv;
+ gboolean conv;
+ if (! (conv = g_time_val_from_iso8601 (bv->bv_val,
+ &tv))) {
+ /* Add the 'T' char */
+ gchar *tmp, *str;
+ gint i, len;
+ str = bv->bv_val;
+ len = strlen (str);
+ if (len > 8) {
+ tmp = g_new (gchar, len + 2);
+ for (i = 0; i < 8; i++)
+ tmp[i] = str[i];
+ tmp [8] = 'T';
+ for (i = 9; str[i]; i++)
+ tmp[i] = str[i-1];
+ tmp[i] = 0;
+ conv = g_time_val_from_iso8601 (tmp, &tv);
+ g_free (tmp);
+ }
+ }
+ if (conv) {
+ struct tm *ptm;
+ ptm = localtime (&(tv.tv_sec));
+ if (type == GDA_TYPE_TIMESTAMP) {
+ GdaTimestamp ts;
+ ts.year = ptm->tm_year + 1900;
+ ts.month = ptm->tm_mon + 1;
+ ts.day = ptm->tm_mday;
+ ts.hour = ptm->tm_hour;
+ ts.minute = ptm->tm_min;
+ ts.second = ptm->tm_sec;
+ ts.timezone = GDA_TIMEZONE_INVALID;
+ value = gda_value_new (type);
+ gda_value_set_timestamp (value, &ts);
+ }
+ else {
+ GDate *date;
+ date = g_date_new ();
+ g_date_set_time_val (date, &tv);
+ value = gda_value_new (type);
+ g_value_take_boxed (value, date);
+ }
+ }
+ }
+ else if (type == GDA_TYPE_BINARY) {
+ GdaBinary *bin;
+ bin = g_new (GdaBinary, 1);
+ bin->data = g_new (guchar, bv->bv_len);
+
+ bin->binary_length = bv->bv_len;
+ memcpy (bin->data, bv->bv_val,
+ sizeof (gchar) * bin->binary_length);
+ value = gda_value_new (GDA_TYPE_BINARY);
+ gda_value_take_binary (value, bin);
+ }
+ else
+ value = gda_value_new_from_string (bv->bv_val, type);
+
+ return value;
+}
+
+/*
+ * make sure we respect http://www.faqs.org/rfcs/rfc2253.html
+ */
+static gchar *
+rewrite_dn_component (const char *str, guint len)
+{
+ guint i;
+ gint nbrewrite = 0;
+ for (i = 0; i < len; i++) {
+ // "," / "=" / "+" / "<" / ">" / "#" / ";"
+ if ((str[i] == ',') || (str[i] == '=') || (str[i] == '+') ||
+ (str[i] == '<') || (str[i] == '>') || (str[i] == '#') || (str[i] == ';'))
+ nbrewrite++;
+ }
+ if (nbrewrite == 0)
+ return NULL;
+
+ gchar *tmp, *ptr;
+ tmp = g_new (gchar, len + 2*nbrewrite + 1);
+ for (i = 0, ptr = tmp; i < len; i++) {
+ if ((str[i] == ',') || (str[i] == '=') || (str[i] == '+') ||
+ (str[i] == '<') || (str[i] == '>') || (str[i] == '#') || (str[i] == ';')) {
+ int t;
+ *ptr = '\\';
+ ptr++;
+ t = str[i] / 16;
+ if (t < 10)
+ *ptr = '0' + t;
+ else
+ *ptr = 'A' + t - 10;
+ ptr++;
+ t = str[i] % 16;
+ if (t < 10)
+ *ptr = '0' + t;
+ else
+ *ptr = 'A' + t - 10;
+ }
+ else
+ *ptr = str[i];
+ ptr++;
+ }
+ *ptr = 0;
+ return tmp;
+}
+
+/**
+ * _gda_Rdn2str:
+ *
+ * Returns: a new string
+ */
+static gchar *
+_gda_Rdn2str (LDAPRDN rdn)
+{
+ if (!rdn)
+ return NULL;
+ gint i;
+ GString *string = NULL;
+ for (i = 0; rdn[i]; i++) {
+ LDAPAVA *ava = rdn [i];
+ if (g_utf8_validate (ava->la_attr.bv_val, ava->la_attr.bv_len, NULL) &&
+ g_utf8_validate (ava->la_value.bv_val, ava->la_value.bv_len, NULL)) {
+ gchar *tmp;
+ if (string)
+ g_string_append_c (string, '+');
+ else
+ string = g_string_new ("");
+
+ /* attr name */
+ tmp = rewrite_dn_component (ava->la_attr.bv_val, ava->la_attr.bv_len);
+ if (tmp) {
+ g_string_append (string, tmp);
+ g_free (tmp);
+ }
+ else
+ g_string_append_len (string, ava->la_attr.bv_val, ava->la_attr.bv_len);
+ g_string_append_c (string, '=');
+
+ /* attr value */
+ tmp = rewrite_dn_component (ava->la_value.bv_val, ava->la_value.bv_len);
+ if (tmp) {
+ g_string_append (string, tmp);
+ g_free (tmp);
+ }
+ else
+ g_string_append_len (string, ava->la_value.bv_val, ava->la_value.bv_len);
+ }
+ else {
+ if (string) {
+ g_string_free (string, TRUE);
+ return NULL;
+ }
+ }
+ }
+ return g_string_free (string, FALSE);
+}
+
+
+/**
+ * _gda_dn2str:
+ *
+ * Returns: a new string
+ */
+static gchar *
+_gda_dn2str (LDAPDN dn)
+{
+ if (!dn)
+ return NULL;
+
+ gint i;
+ GString *string = NULL;
+ for (i = 0; dn[i]; i++) {
+ LDAPRDN rdn = dn[i];
+ gchar *tmp;
+ tmp = _gda_Rdn2str (rdn);
+ if (tmp) {
+ if (string)
+ g_string_append_c (string, ',');
+ else
+ string = g_string_new ("");
+ g_string_append (string, tmp);
+ g_free (tmp);
+ }
+ else {
+ if (string) {
+ g_string_free (string, TRUE);
+ return NULL;
+ }
+ }
+ }
+ return g_string_free (string, FALSE);
+}
+
+/*
+ * parse_dn
+ *
+ * Parse and reconstruct @attr if @out_userdn is not %NULL
+ *
+ * Returns: %TRUE if all OK
+ */
+gboolean
+gda_ldap_parse_dn (const char *attr, gchar **out_userdn)
+{
+ LDAPDN tmpDN;
+
+ if (out_userdn)
+ *out_userdn = NULL;
+
+ /* decoding */
+ if (ldap_str2dn (attr, &tmpDN, LDAP_DN_FORMAT_LDAPV3) != LDAP_SUCCESS) {
+ if (ldap_str2dn (attr, &tmpDN, LDAP_DN_FORMAT_LDAPV2) != LDAP_SUCCESS) {
+ if (ldap_str2dn (attr, &tmpDN, LDAP_DN_FORMAT_DCE) != LDAP_SUCCESS)
+ return FALSE;
+ }
+ }
+
+ if (out_userdn) {
+ gchar *userdn;
+ userdn = _gda_dn2str (tmpDN);
+ ldap_dnfree (tmpDN);
+ if (userdn)
+ *out_userdn = userdn;
+ else
+ return FALSE;
+ }
+ else
+ ldap_dnfree (tmpDN);
+
+ return TRUE;
+}
+
+/*
+ * Proxy functions
+ */
+
+static gint
+attr_array_sort_func (gconstpointer a, gconstpointer b)
+{
+ GdaLdapAttribute *att1, *att2;
+ att1 = *((GdaLdapAttribute**) a);
+ att2 = *((GdaLdapAttribute**) b);
+ return strcmp (att1->attr_name, att2->attr_name);
+}
+
+GdaLdapEntry *
+gdaprov_ldap_describe_entry (GdaLdapConnection *cnc, const gchar *dn, GError **error)
+{
+ LdapConnectionData *cdata;
+ g_return_val_if_fail (GDA_IS_LDAP_CONNECTION (cnc), NULL);
+ g_return_val_if_fail (!dn || (dn && *dn), NULL);
+
+ cdata = (LdapConnectionData*) gda_virtual_connection_internal_get_provider_data (GDA_VIRTUAL_CONNECTION (cnc));
+ if (!cdata)
+ return NULL;
+
+ int res;
+ LDAPMessage *msg = NULL;
+ const gchar *real_dn;
+ real_dn = dn ? dn : cdata->base_dn;
+ retry:
+ res = ldap_search_ext_s (cdata->handle, real_dn, LDAP_SCOPE_BASE,
+ "(objectClass=*)", NULL, 0,
+ NULL, NULL, NULL, -1,
+ &msg);
+ switch (res) {
+ case LDAP_SUCCESS:
+ case LDAP_NO_SUCH_OBJECT: {
+ gint nb_entries;
+ LDAPMessage *ldap_row;
+ char *attr;
+ BerElement* ber;
+ GdaLdapEntry *lentry;
+ GArray *array = NULL;
+
+ nb_entries = ldap_count_entries (cdata->handle, msg);
+ if (nb_entries == 0) {
+ ldap_msgfree (msg);
+ return NULL;
+ }
+ else if (nb_entries > 1) {
+ g_set_error (error, 0, 0,
+ _("LDAP server returned more than one entry with DN '%s'"), real_dn);
+ return NULL;
+ }
+
+ lentry = g_new0 (GdaLdapEntry, 1);
+ lentry->dn = g_strdup (real_dn);
+ lentry->attributes_hash = g_hash_table_new (g_str_hash, g_str_equal);
+ array = g_array_new (TRUE, FALSE, sizeof (GdaLdapAttribute*));
+ ldap_row = ldap_first_entry (cdata->handle, msg);
+ for (attr = ldap_first_attribute (cdata->handle, ldap_row, &ber);
+ attr;
+ attr = ldap_next_attribute (cdata->handle, ldap_row, ber)) {
+ BerValue **bvals;
+ GArray *varray = NULL;
+ bvals = ldap_get_values_len (cdata->handle, ldap_row, attr);
+ if (bvals) {
+ gint i;
+ for (i = 0; bvals [i]; i++) {
+ if (!varray)
+ varray = g_array_new (TRUE, FALSE, sizeof (GValue *));
+ GValue *value;
+ GType type;
+ type = gda_ldap_get_g_type (cdata, attr, NULL);
+ /*g_print ("Type for attr %s is %s\n", attr, gda_g_type_to_string (type)); */
+ value = gda_ldap_attr_value_to_g_value (cdata, type, bvals[i]);
+ g_array_append_val (varray, value);
+ }
+ ldap_value_free_len (bvals);
+ }
+ if (varray) {
+ GdaLdapAttribute *lattr = NULL;
+ lattr = g_new0 (GdaLdapAttribute, 1);
+ lattr->attr_name = g_strdup (attr);
+ lattr->values = (GValue**) varray->data;
+ lattr->nb_values = varray->len;
+ g_array_free (varray, FALSE);
+
+ g_array_append_val (array, lattr);
+ g_hash_table_insert (lentry->attributes_hash, lattr->attr_name, lattr);
+ }
+ ldap_memfree (attr);
+ }
+ if (ber)
+ ber_free (ber, 0);
+ ldap_msgfree (msg);
+ if (array) {
+ g_array_sort (array, (GCompareFunc) attr_array_sort_func);
+ lentry->attributes = (GdaLdapAttribute**) array->data;
+ lentry->nb_attributes = array->len;
+ g_array_free (array, FALSE);
+ }
+ return lentry;
+ }
+ case LDAP_SERVER_DOWN: {
+ gint i;
+ for (i = 0; i < 5; i++) {
+ if (gda_ldap_silently_rebind (cdata))
+ goto retry;
+ g_usleep (G_USEC_PER_SEC * 2);
+ }
+ }
+ default: {
+ /* error */
+ int ldap_errno;
+ ldap_get_option (cdata->handle, LDAP_OPT_ERROR_NUMBER, &ldap_errno);
+ g_set_error (error, GDA_DATA_MODEL_ERROR, GDA_DATA_MODEL_OTHER_ERROR,
+ "%s", ldap_err2string(ldap_errno));
+ return NULL;
+ }
+ }
+}
+
+static gint
+entry_array_sort_func (gconstpointer a, gconstpointer b)
+{
+ GdaLdapEntry *e1, *e2;
+ e1 = *((GdaLdapEntry**) a);
+ e2 = *((GdaLdapEntry**) b);
+ return strcmp (e2->dn, e1->dn);
+}
+
+GdaLdapEntry **
+gdaprov_ldap_get_entry_children (GdaLdapConnection *cnc, const gchar *dn, gchar **attributes, GError **error)
+{
+ LdapConnectionData *cdata;
+ g_return_val_if_fail (GDA_IS_LDAP_CONNECTION (cnc), NULL);
+ g_return_val_if_fail (!dn || (dn && *dn), NULL);
+
+ cdata = (LdapConnectionData*) gda_virtual_connection_internal_get_provider_data (GDA_VIRTUAL_CONNECTION (cnc));
+ if (!cdata)
+ return NULL;
+
+ int res;
+ LDAPMessage *msg = NULL;
+ retry:
+ res = ldap_search_ext_s (cdata->handle, dn ? dn : cdata->base_dn, LDAP_SCOPE_ONELEVEL,
+ "(objectClass=*)", attributes, 0,
+ NULL, NULL, NULL, -1,
+ &msg);
+
+ switch (res) {
+ case LDAP_SUCCESS:
+ case LDAP_NO_SUCH_OBJECT: {
+ LDAPMessage *ldap_row;
+ GArray *children;
+
+ children = g_array_new (TRUE, FALSE, sizeof (GdaLdapEntry *));
+ for (ldap_row = ldap_first_entry (cdata->handle, msg);
+ ldap_row;
+ ldap_row = ldap_next_entry (cdata->handle, ldap_row)) {
+ char *attr;
+ GdaLdapEntry *lentry = NULL;
+ attr = ldap_get_dn (cdata->handle, ldap_row);
+ if (attr) {
+ gchar *userdn = NULL;
+ if (gda_ldap_parse_dn (attr, &userdn)) {
+ lentry = g_new0 (GdaLdapEntry, 1);
+ lentry->dn = userdn;
+ }
+ ldap_memfree (attr);
+ }
+
+ if (!lentry) {
+ gint i;
+ for (i = 0; i < children->len; i++) {
+ GdaLdapEntry *lentry;
+ lentry = g_array_index (children, GdaLdapEntry*, i);
+ gda_ldap_entry_free (lentry);
+ }
+ g_array_free (children, TRUE);
+ children = NULL;
+ g_set_error (error, 0, 0,
+ _("Could not parse distinguished name returned by LDAP server"));
+ break;
+ }
+ else if (attributes) {
+ BerElement* ber;
+ GArray *array; /* array of GdaLdapAttribute pointers */
+ lentry->attributes_hash = g_hash_table_new (g_str_hash, g_str_equal);
+ array = g_array_new (TRUE, FALSE, sizeof (GdaLdapAttribute*));
+ for (attr = ldap_first_attribute (cdata->handle, ldap_row, &ber);
+ attr;
+ attr = ldap_next_attribute (cdata->handle, ldap_row, ber)) {
+ BerValue **bvals;
+ GArray *varray = NULL;
+ bvals = ldap_get_values_len (cdata->handle, ldap_row, attr);
+ if (bvals) {
+ gint i;
+ for (i = 0; bvals [i]; i++) {
+ if (!varray)
+ varray = g_array_new (TRUE, FALSE, sizeof (GValue *));
+ GValue *value;
+ GType type;
+ type = gda_ldap_get_g_type (cdata, attr, NULL);
+ /*g_print ("%d Type for attr %s is %s\n", i, attr, gda_g_type_to_string (type));*/
+ value = gda_ldap_attr_value_to_g_value (cdata, type, bvals[i]);
+ g_array_append_val (varray, value);
+ }
+ ldap_value_free_len (bvals);
+ }
+ if (varray) {
+ GdaLdapAttribute *lattr = NULL;
+ lattr = g_new0 (GdaLdapAttribute, 1);
+ lattr->attr_name = g_strdup (attr);
+ lattr->values = (GValue**) varray->data;
+ lattr->nb_values = varray->len;
+ g_array_free (varray, FALSE);
+
+ g_array_append_val (array, lattr);
+ g_hash_table_insert (lentry->attributes_hash, lattr->attr_name, lattr);
+ }
+ ldap_memfree (attr);
+ }
+ if (ber)
+ ber_free (ber, 0);
+ if (array) {
+ g_array_sort (array, (GCompareFunc) attr_array_sort_func);
+ lentry->attributes = (GdaLdapAttribute**) array->data;
+ lentry->nb_attributes = array->len;
+ g_array_free (array, FALSE);
+ }
+ }
+ g_array_append_val (children, lentry);
+ }
+ ldap_msgfree (msg);
+ if (children) {
+ g_array_sort (children, (GCompareFunc) entry_array_sort_func);
+ return (GdaLdapEntry**) g_array_free (children, FALSE);
+ }
+ else
+ return NULL;
+ }
+ case LDAP_SERVER_DOWN: {
+ gint i;
+ for (i = 0; i < 5; i++) {
+ if (gda_ldap_silently_rebind (cdata))
+ goto retry;
+ g_usleep (G_USEC_PER_SEC * 2);
+ }
+ }
+ default: {
+ /* error */
+ int ldap_errno;
+ ldap_get_option (cdata->handle, LDAP_OPT_ERROR_NUMBER, &ldap_errno);
+ g_set_error (error, GDA_DATA_MODEL_ERROR, GDA_DATA_MODEL_OTHER_ERROR,
+ "%s", ldap_err2string(ldap_errno));
+ return NULL;
+ }
+ }
+}
+
+gchar **
+gdaprov_ldap_dn_split (const gchar *dn, gboolean all)
+{
+ LDAPDN tmpDN;
+ GArray *array;
+ gint imax = G_MAXINT;
+
+ g_return_val_if_fail (dn && *dn, NULL);
+
+ /*g_print ("%s (%s, %d)\n", __FUNCTION__, dn, all);*/
+
+ /* decoding */
+ if (ldap_str2dn (dn, &tmpDN, LDAP_DN_FORMAT_LDAPV3) != LDAP_SUCCESS) {
+ if (ldap_str2dn (dn, &tmpDN, LDAP_DN_FORMAT_LDAPV2) != LDAP_SUCCESS) {
+ if (ldap_str2dn (dn, &tmpDN, LDAP_DN_FORMAT_DCE) != LDAP_SUCCESS)
+ return NULL;
+ }
+ }
+
+ /* encoding */
+ array = g_array_new (TRUE, FALSE, sizeof (gchar*));
+ if (!all)
+ imax = 1;
+
+ gint i;
+ LDAPRDN *rdn = tmpDN;
+ for (i = 0; rdn [i] && (i < imax); i++) {
+ gchar *tmp;
+ tmp = _gda_Rdn2str (rdn [i]);
+ if (tmp) {
+ g_array_append_val (array, tmp);
+ /*g_print ("\t[%s]\n", value);*/
+ }
+ else
+ goto onerror;
+ }
+
+ if (!all && (i == 1) && rdn [1]) {
+ gchar *tmp;
+ tmp = _gda_dn2str (rdn+1);
+ if (tmp) {
+ g_array_append_val (array, tmp);
+ /*g_print ("\t[%s]\n", value);*/
+ }
+ else
+ goto onerror;
+ }
+ ldap_dnfree (tmpDN);
+
+ return (gchar**) g_array_free (array, FALSE);
+
+ onerror:
+ for (i = 0; i < array->len; i++) {
+ gchar *tmp;
+ tmp = g_array_index (array, gchar*, i);
+ g_free (tmp);
+ }
+ g_array_free (array, TRUE);
+ return NULL;
+}
+
+gboolean
+gdaprov_ldap_is_dn (const gchar *dn)
+{
+ LDAPDN tmpDN;
+
+ g_return_val_if_fail (dn && *dn, FALSE);
+ if (ldap_str2dn (dn, &tmpDN, LDAP_DN_FORMAT_LDAPV3) != LDAP_SUCCESS) {
+ if (ldap_str2dn (dn, &tmpDN, LDAP_DN_FORMAT_LDAPV2) != LDAP_SUCCESS) {
+ if (ldap_str2dn (dn, &tmpDN, LDAP_DN_FORMAT_DCE) != LDAP_SUCCESS)
+ return FALSE;
+ }
+ }
+ ldap_dnfree (tmpDN);
+ return TRUE;
+}
+
+const gchar *
+gdaprov_ldap_get_base_dn (GdaLdapConnection *cnc)
+{
+ LdapConnectionData *cdata;
+ g_return_val_if_fail (GDA_IS_LDAP_CONNECTION (cnc), NULL);
+
+ cdata = (LdapConnectionData*) gda_virtual_connection_internal_get_provider_data (GDA_VIRTUAL_CONNECTION (cnc));
+ if (!cdata)
+ return NULL;
+ else
+ return cdata->base_dn;
+}
diff --git a/providers/ldap/gda-ldap-util.h b/providers/ldap/gda-ldap-util.h
new file mode 100644
index 0000000..57bd095
--- /dev/null
+++ b/providers/ldap/gda-ldap-util.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2011 The GNOME Foundation
+ *
+ * AUTHORS:
+ * Vivien Malerba <malerba gnome-db org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * Some data copied from GQ's sources and transformed
+ */
+
+#ifndef __GDA_LDAP_UTIL_H__
+#define __GDA_LDAP_UTIL_H__
+
+#include <glib.h>
+#include "gda-ldap.h"
+
+/*
+ * Attributes
+ */
+typedef struct {
+ gchar *oid;
+ gchar *descr;
+ GType gtype;
+} LdapAttrType;
+
+typedef struct {
+ gchar *name;
+ LdapAttrType *type; /* never NULL */
+ gboolean single_value;
+} LdapAttribute;
+
+LdapAttrType *gda_ldap_get_type_info (const gchar *oid);
+LdapAttribute *gda_ldap_get_attr_info (LdapConnectionData *cdata, const gchar *attribute);
+GType gda_ldap_get_g_type (LdapConnectionData *cdata, const gchar *attribute, const gchar *specified_gtype);
+
+/*
+ * Misc.
+ */
+GValue *gda_ldap_attr_value_to_g_value (LdapConnectionData *cdata, GType type, BerValue *bv);
+gboolean gda_ldap_parse_dn (const char *attr, gchar **out_userdn);
+
+#endif
diff --git a/providers/ldap/gda-ldap.h b/providers/ldap/gda-ldap.h
new file mode 100644
index 0000000..8a2fe13
--- /dev/null
+++ b/providers/ldap/gda-ldap.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2011 The GNOME Foundation
+ *
+ * AUTHORS:
+ * Vivien Malerba <malerba gnome-db org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __GDA_LDAP_H__
+#define __GDA_LDAP_H__
+
+/*
+ * Provider name
+ */
+#define LDAP_PROVIDER_NAME "Ldap"
+
+#include <ldap.h>
+#include <ldap_schema.h>
+#include <glib.h>
+
+/*
+ * Provider's specific connection data
+ */
+typedef struct {
+ LDAP *handle;
+ gchar *base_dn;
+ gchar *server_version;
+ gchar *url;
+ gchar *user;
+ gchar *pass;
+
+ GHashTable *attributes_hash; /* key = attribute name, value = a #LdapAttribute */
+ gchar *attributes_cache_file;
+
+ GSList *top_classes; /* list of #LdapClass (no ref held) which have no parent */
+ GHashTable *classes_hash; /* key = class name, value = a #LdapClass */
+} LdapConnectionData;
+
+gboolean gda_ldap_silently_rebind (LdapConnectionData *cdata);
+
+#endif
diff --git a/providers/ldap/gdaprov-data-model-ldap.c b/providers/ldap/gdaprov-data-model-ldap.c
new file mode 100644
index 0000000..b65037f
--- /dev/null
+++ b/providers/ldap/gdaprov-data-model-ldap.c
@@ -0,0 +1,1447 @@
+/*
+ * Copyright (C) 2011 The GNOME Foundation
+ *
+ * AUTHORS:
+ * Vivien Malerba <malerba gnome-db org>
+ *
+ * This Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+#include <glib/gi18n-lib.h>
+#include <libgda/gda-data-model-ldap.h>
+#include <libgda/gda-connection.h>
+#include <libgda/gda-data-model-iter.h>
+#include <libgda/gda-holder.h>
+#include <libgda/gda-util.h>
+#include <libgda/sqlite/virtual/gda-virtual-connection.h>
+#include <libgda/sqlite/virtual/gda-ldap-connection.h>
+#include "gda-ldap.h"
+#include "gda-ldap-util.h"
+#include "gdaprov-data-model-ldap.h"
+
+#define GDA_DEBUG_SUBSEARCHES
+#undef GDA_DEBUG_SUBSEARCHES
+
+/*
+ * What to do in case of a multi value in a cell
+ */
+typedef enum {
+ MULTIPLE_VALUE_ACTION_SET_NULL,
+ MULTIPLE_VALUE_ACTION_CSV_STRING,
+ MULTIPLE_VALUE_ACTION_MULTIPLY,
+ MULTIPLE_VALUE_ACTION_SET_INVALID,
+ MULTIPLE_VALUE_ACTION_FIRST,
+ MULTIPLE_VALUE_ACTION_CONCAT
+} MultipleValueAction;
+
+typedef struct _LdapPart LdapPart;
+struct _LdapPart {
+ gchar *base_dn;
+ GdaLdapSearchScope scope;
+ gboolean executed; /* %TRUE if @ldap_msg of @children have already had the
+ * opportunity of being computed */
+
+ /* result of execution, both %NULL if data was truncated when executing */
+ LDAPMessage *ldap_msg;
+ gint nb_entries;
+ LDAPMessage *ldap_row; /* no ref! */
+
+ /* tree-like structure */
+ GSList *children; /* list of #LdapPart, ref held there */
+ LdapPart *parent; /* no ref held */
+};
+#define LDAP_PART(x) ((LdapPart*)(x))
+
+static LdapPart *ldap_part_new (LdapPart *parent, const gchar *base_dn, GdaLdapSearchScope scope);
+static void ldap_part_free (LdapPart *part);
+static gboolean ldap_part_split (LdapPart *part, GdaDataModelLdap *model, gboolean *out_error);
+static LdapPart *ldap_part_next (LdapPart *part, gboolean executed);
+#ifdef GDA_DEBUG_SUBSEARCHES
+static void ldap_part_dump (LdapPart *part);
+#endif
+
+static void add_exception (GdaDataModelLdap *model, GError *e);
+
+typedef struct {
+ GdaHolder *holder;
+ gint index;
+ GArray *values; /* array of #GValue, or %NULL on error */
+} ColumnMultiplier;
+static ColumnMultiplier *column_multiplier_new (GdaHolder *holder,
+ const GValue *value);
+
+typedef struct {
+ GArray *cms; /* array of ColumnMultiplier pointers, stores in reverse order */
+} RowMultiplier;
+static RowMultiplier *row_multiplier_new (void);
+static gboolean row_multiplier_index_next (RowMultiplier *rm);
+static void row_multiplier_free (RowMultiplier *rm);
+static void row_multiplier_set_holders (RowMultiplier *rm);
+
+struct _GdaDataModelLdapPrivate {
+ GdaConnection *cnc;
+ gchar *base_dn;
+ gchar *filter;
+ GArray *attributes;
+ GdaLdapSearchScope scope;
+ MultipleValueAction default_mv_action;
+ GList *columns;
+ GArray *column_mv_actions; /* array of #MultipleValueAction, notincluding column 0 */
+ gint n_columns; /* length of @columns */
+ gint n_rows;
+ gboolean truncated;
+
+ gint iter_row;
+ LdapPart *top_exec; /* ref held */
+ LdapPart *current_exec; /* no ref held, only a pointer in the @top_exec tree */
+
+ RowMultiplier *row_mult;
+
+ GArray *exceptions; /* array of GError pointers */
+};
+
+/* properties */
+enum {
+ PROP_0,
+ PROP_CNC,
+ PROP_BASE,
+ PROP_FILTER,
+ PROP_ATTRIBUTES,
+ PROP_SCOPE
+};
+
+static void gda_data_model_ldap_class_init (GdaDataModelLdapClass *klass);
+static void gda_data_model_ldap_init (GdaDataModelLdap *model,
+ GdaDataModelLdapClass *klass);
+static void gda_data_model_ldap_dispose (GObject *object);
+
+static void gda_data_model_ldap_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gda_data_model_ldap_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static GList *_ldap_compute_columns (GdaConnection *cnc, const gchar *attributes,
+ GArray **out_attrs_array,
+ MultipleValueAction default_mva, GArray **out_mv_actions);
+
+/* GdaDataModel interface */
+static void gda_data_model_ldap_data_model_init (GdaDataModelIface *iface);
+static gint gda_data_model_ldap_get_n_rows (GdaDataModel *model);
+static gint gda_data_model_ldap_get_n_columns (GdaDataModel *model);
+static GdaColumn *gda_data_model_ldap_describe_column (GdaDataModel *model, gint col);
+static GdaDataModelAccessFlags gda_data_model_ldap_get_access_flags(GdaDataModel *model);
+static gboolean gda_data_model_ldap_iter_next (GdaDataModel *model, GdaDataModelIter *iter);
+static GdaValueAttribute gda_data_model_ldap_get_attributes_at (GdaDataModel *model, gint col, gint row);
+static GError **gda_data_model_ldap_get_exceptions (GdaDataModel *model);
+
+static GObjectClass *parent_class = NULL;
+#define CLASS(model) (GDA_DATA_MODEL_LDAP_CLASS (G_OBJECT_GET_CLASS (model)))
+
+/*
+ * Object init and dispose
+ */
+static void
+gda_data_model_ldap_data_model_init (GdaDataModelIface *iface)
+{
+ iface->i_get_n_rows = gda_data_model_ldap_get_n_rows;
+ iface->i_get_n_columns = gda_data_model_ldap_get_n_columns;
+ iface->i_describe_column = gda_data_model_ldap_describe_column;
+ iface->i_get_access_flags = gda_data_model_ldap_get_access_flags;
+ iface->i_get_value_at = NULL;
+ iface->i_get_attributes_at = gda_data_model_ldap_get_attributes_at;
+
+ iface->i_create_iter = NULL;
+ iface->i_iter_at_row = NULL;
+ iface->i_iter_next = gda_data_model_ldap_iter_next;
+ iface->i_iter_prev = NULL;
+
+ iface->i_set_value_at = NULL;
+ iface->i_iter_set_value = NULL;
+ iface->i_set_values = NULL;
+ iface->i_append_values = NULL;
+ iface->i_append_row = NULL;
+ iface->i_remove_row = NULL;
+ iface->i_find_row = NULL;
+
+ iface->i_set_notify = NULL;
+ iface->i_get_notify = NULL;
+ iface->i_send_hint = NULL;
+
+ iface->i_get_exceptions = gda_data_model_ldap_get_exceptions;
+}
+
+static void
+gda_data_model_ldap_init (GdaDataModelLdap *model,
+ G_GNUC_UNUSED GdaDataModelLdapClass *klass)
+{
+ GdaColumn *col;
+
+ g_return_if_fail (GDA_IS_DATA_MODEL_LDAP (model));
+
+ model->priv = g_new0 (GdaDataModelLdapPrivate, 1);
+ model->priv->cnc = NULL;
+ model->priv->filter = g_strdup ("(objectClass=*)");
+ model->priv->iter_row = -1;
+ model->priv->default_mv_action = MULTIPLE_VALUE_ACTION_SET_INVALID;
+ model->priv->top_exec = NULL;
+ model->priv->current_exec = NULL;
+ model->priv->attributes = NULL;
+ model->priv->truncated = FALSE;
+ model->priv->exceptions = NULL;
+ model->priv->row_mult = NULL;
+
+ /* add the "dn" column */
+ col = gda_column_new ();
+ gda_column_set_name (col, "dn");
+ gda_column_set_g_type (col, G_TYPE_STRING);
+ gda_column_set_allow_null (col, FALSE);
+ gda_column_set_description (col, _("Distinguished name"));
+ model->priv->columns = g_list_prepend (NULL, col);
+ model->priv->column_mv_actions = g_array_new (FALSE, FALSE, sizeof (MultipleValueAction));
+
+ model->priv->n_columns = g_list_length (model->priv->columns);
+ model->priv->scope = GDA_LDAP_SEARCH_BASE;
+}
+
+static void
+gda_data_model_ldap_class_init (GdaDataModelLdapClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ /* properties */
+ object_class->set_property = gda_data_model_ldap_set_property;
+ object_class->get_property = gda_data_model_ldap_get_property;
+ g_object_class_install_property (object_class, PROP_CNC,
+ g_param_spec_object ("cnc", NULL, "LDAP connection",
+ GDA_TYPE_LDAP_CONNECTION,
+ G_PARAM_READABLE | G_PARAM_WRITABLE |
+ G_PARAM_CONSTRUCT_ONLY));
+ g_object_class_install_property (object_class, PROP_BASE,
+ g_param_spec_string ("base", NULL, "Base DN", NULL,
+ G_PARAM_READABLE | G_PARAM_WRITABLE |
+ G_PARAM_CONSTRUCT_ONLY));
+ g_object_class_install_property (object_class, PROP_FILTER,
+ g_param_spec_string ("filter", NULL, "LDAP filter", NULL,
+ G_PARAM_READABLE | G_PARAM_WRITABLE |
+ G_PARAM_CONSTRUCT_ONLY));
+ g_object_class_install_property (object_class, PROP_ATTRIBUTES,
+ g_param_spec_string ("attributes", NULL, "LDAP attributes", NULL,
+ G_PARAM_WRITABLE |
+ G_PARAM_CONSTRUCT_ONLY));
+ g_object_class_install_property (object_class, PROP_SCOPE,
+ g_param_spec_int ("scope", NULL, "LDAP search scope",
+ GDA_LDAP_SEARCH_BASE,
+ GDA_LDAP_SEARCH_SUBTREE,
+ GDA_LDAP_SEARCH_BASE,
+ G_PARAM_WRITABLE | G_PARAM_READABLE |
+ G_PARAM_CONSTRUCT_ONLY));
+
+ /* virtual functions */
+ object_class->dispose = gda_data_model_ldap_dispose;
+}
+
+static void
+gda_data_model_ldap_dispose (GObject * object)
+{
+ GdaDataModelLdap *model = (GdaDataModelLdap *) object;
+
+ g_return_if_fail (GDA_IS_DATA_MODEL_LDAP (model));
+
+ if (model->priv) {
+ if (model->priv->row_mult)
+ row_multiplier_free (model->priv->row_mult);
+ if (model->priv->cnc)
+ g_object_unref (model->priv->cnc);
+ if (model->priv->columns) {
+ g_list_foreach (model->priv->columns, (GFunc) g_object_unref, NULL);
+ g_list_free (model->priv->columns);
+ model->priv->columns = NULL;
+ }
+ if (model->priv->attributes) {
+ gint i;
+ for (i = 0; i < model->priv->attributes->len; i++) {
+ gchar *tmp;
+ tmp = g_array_index (model->priv->attributes, gchar*, i);
+ g_free (tmp);
+ }
+ g_array_free (model->priv->attributes, TRUE);
+ }
+ if (model->priv->column_mv_actions)
+ g_array_free (model->priv->column_mv_actions, TRUE);
+
+ if (model->priv->top_exec)
+ ldap_part_free (model->priv->top_exec);
+
+ g_free (model->priv->base_dn);
+ g_free (model->priv->filter);
+
+ if (model->priv->exceptions) {
+ gint i;
+ for (i = 0; i < model->priv->exceptions->len; i++) {
+ GError *e;
+ e = g_array_index (model->priv->exceptions, GError*, i);
+ g_error_free (e);
+ }
+ g_array_free (model->priv->exceptions, TRUE);
+ }
+
+ g_free (model->priv);
+ model->priv = NULL;
+ }
+
+ parent_class->dispose (object);
+}
+
+GType
+gdaprov_data_model_ldap_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0)) {
+ static GStaticMutex registering = G_STATIC_MUTEX_INIT;
+ static const GTypeInfo info = {
+ sizeof (GdaDataModelLdapClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) gda_data_model_ldap_class_init,
+ NULL,
+ NULL,
+ sizeof (GdaDataModelLdap),
+ 0,
+ (GInstanceInitFunc) gda_data_model_ldap_init,
+ 0
+ };
+ static const GInterfaceInfo data_model_info = {
+ (GInterfaceInitFunc) gda_data_model_ldap_data_model_init,
+ NULL,
+ NULL
+ };
+
+ g_static_mutex_lock (®istering);
+ if (type == 0) {
+ type = g_type_register_static (G_TYPE_OBJECT, "GdaDataModelLdap", &info, 0);
+ g_type_add_interface_static (type, GDA_TYPE_DATA_MODEL, &data_model_info);
+ }
+ g_static_mutex_unlock (®istering);
+ }
+ return type;
+}
+
+static void
+gda_data_model_ldap_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GdaDataModelLdap *model;
+ const gchar *string;
+
+ model = GDA_DATA_MODEL_LDAP (object);
+ if (model->priv) {
+ switch (param_id) {
+ case PROP_CNC: {
+ GdaConnection *cnc;
+ cnc = g_value_get_object (value);
+ if (cnc) {
+ if (g_object_get_data ((GObject*) cnc,
+ "__gda_connection_LDAP") != (gpointer) 0x01) {
+ g_warning ("cnc is not an LDAP connection");
+ break;
+ }
+ model->priv->cnc = g_object_ref (cnc);
+ }
+ break;
+ }
+ case PROP_BASE:
+ string = g_value_get_string (value);
+ if (string)
+ model->priv->base_dn = g_strdup (string);
+ break;
+ case PROP_FILTER:
+ string = g_value_get_string (value);
+ if (string) {
+ g_free (model->priv->filter);
+ model->priv->filter = g_strdup (string);
+ }
+ break;
+ case PROP_ATTRIBUTES: {
+ const gchar *csv;
+ csv = g_value_get_string (value);
+ if (csv && *csv) {
+ if (model->priv->columns) {
+ g_list_foreach (model->priv->columns, (GFunc) g_object_unref, NULL);
+ g_list_free (model->priv->columns);
+ }
+ if (model->priv->column_mv_actions) {
+ g_array_free (model->priv->column_mv_actions, TRUE);
+ model->priv->column_mv_actions = NULL;
+ }
+
+ model->priv->columns = _ldap_compute_columns (model->priv->cnc, csv,
+ &model->priv->attributes,
+ model->priv->default_mv_action,
+ &model->priv->column_mv_actions);
+ model->priv->n_columns = g_list_length (model->priv->columns);
+ }
+ break;
+ }
+ case PROP_SCOPE:
+ model->priv->scope = g_value_get_int (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+ }
+}
+
+static void
+gda_data_model_ldap_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GdaDataModelLdap *model;
+
+ model = GDA_DATA_MODEL_LDAP (object);
+ if (model->priv) {
+ switch (param_id) {
+ case PROP_CNC:
+ g_value_set_object (value, model->priv->cnc);
+ break;
+ case PROP_BASE:
+ g_value_set_string (value, model->priv->base_dn);
+ break;
+ case PROP_FILTER:
+ g_value_set_string (value, model->priv->filter);
+ break;
+ case PROP_SCOPE:
+ g_value_set_int (value, model->priv->scope);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+ }
+}
+
+GList *
+gdaprov_data_model_ldap_compute_columns (GdaConnection *cnc, const gchar *attributes)
+{
+ return _ldap_compute_columns (cnc, attributes, NULL, MULTIPLE_VALUE_ACTION_SET_INVALID,
+ NULL);
+}
+
+/*
+ * _ldap_compute_columns
+ * @cnc: a #GdaConnection
+ * @attributes: a string
+ * @out_attrs_array: a place to store an array of strings (terminated by a %NULL), or %NULL
+ * @default_mva: the default #MultipleValueAction if none speficied
+ * @out_mv_actions: a place to store an array of MultipleValueAction, or %NULL
+ *
+ * Returns: (transfer full) (element-type GdaColumn): a list of #GdaColumn objects
+ */
+static GList *
+_ldap_compute_columns (GdaConnection *cnc, const gchar *attributes,
+ GArray **out_attrs_array,
+ MultipleValueAction default_mva, GArray **out_mv_actions)
+{
+ gchar **array;
+ gint i;
+ GdaColumn *col;
+ LdapConnectionData *cdata = NULL;
+ GList *columns = NULL;
+ GArray *attrs = NULL, *mva = NULL;
+ GHashTable *colnames; /* key = column name, 0x01 */
+ colnames = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+
+ if (out_attrs_array) {
+ attrs = g_array_new (TRUE, FALSE, sizeof (gchar*));
+ *out_attrs_array = attrs;
+ }
+ if (out_mv_actions) {
+ mva = g_array_new (FALSE, FALSE, sizeof (MultipleValueAction));
+ *out_mv_actions = mva;
+ }
+
+ /* always add the DN column */
+ col = gda_column_new ();
+ gda_column_set_name (col, "dn");
+ gda_column_set_g_type (col, G_TYPE_STRING);
+ gda_column_set_allow_null (col, FALSE);
+ gda_column_set_description (col, _("Distinguished name"));
+ columns = g_list_prepend (NULL, col);
+ g_hash_table_insert (colnames, g_strdup ("dn"), (gpointer) 0x01);
+
+ if (!attributes || !*attributes)
+ return columns;
+
+ /* parse input string */
+ if (cnc) {
+ g_return_val_if_fail (GDA_IS_LDAP_CONNECTION (cnc), NULL);
+ cdata = (LdapConnectionData*) gda_virtual_connection_internal_get_provider_data (GDA_VIRTUAL_CONNECTION (cnc));
+ }
+ array = g_strsplit (attributes, ",", 0);
+ for (i = 0; array [i]; i++) {
+ GType coltype = GDA_TYPE_NULL;
+ gchar **sub, *tmp;
+ const gchar *mvaspec = NULL;
+ MultipleValueAction act = default_mva;
+
+ g_strstrip (array [i]);
+ sub = g_strsplit (array[i], "::", 3);
+ g_strstrip (sub [0]);
+ if (sub [1]) {
+ g_strstrip (sub [1]);
+ if (sub [2]) {
+ g_strstrip (sub [2]);
+ mvaspec = sub [2];
+ }
+ }
+
+ coltype = gda_ldap_get_g_type (cdata, sub [0], sub [1]);
+ tmp = g_strdup (sub [0]);
+ if (attrs)
+ g_array_append_val (attrs, tmp);
+ if (g_hash_table_lookup (colnames, sub [0])) {
+ /* can't have twice the same LDAP attribute */
+ g_strfreev (sub);
+ continue;
+ }
+ col = gda_column_new ();
+ gda_column_set_name (col, sub [0]);
+ g_hash_table_insert (colnames, g_strdup (sub [0]), (gpointer) 0x01);
+ gda_column_set_g_type (col, coltype);
+ gda_column_set_allow_null (col, TRUE);
+ columns = g_list_prepend (columns, col);
+ if (mva) {
+ if (! mvaspec && sub [1] && (gda_g_type_from_string (sub [1]) == G_TYPE_INVALID))
+ mvaspec = sub [1];
+ if (mvaspec) {
+ if ((*mvaspec == '0' && !mvaspec[1]) || !g_ascii_strcasecmp (mvaspec, "null"))
+ act = MULTIPLE_VALUE_ACTION_SET_NULL;
+ else if (!g_ascii_strcasecmp (mvaspec, "csv"))
+ act = MULTIPLE_VALUE_ACTION_CSV_STRING;
+ if ((*mvaspec == '*' && !mvaspec[1]) || !g_ascii_strncasecmp (mvaspec, "mult", 4))
+ act = MULTIPLE_VALUE_ACTION_MULTIPLY;
+ else if (!g_ascii_strcasecmp (mvaspec, "error"))
+ act = MULTIPLE_VALUE_ACTION_SET_INVALID;
+ else if (!strcmp (mvaspec, "1"))
+ act = MULTIPLE_VALUE_ACTION_FIRST;
+ else if (!g_ascii_strcasecmp (mvaspec, "concat"))
+ act = MULTIPLE_VALUE_ACTION_CONCAT;
+ }
+ g_array_append_val (mva, act);
+ }
+ /*g_print ("Defined model column %s (type=>%s) (mva=>%d)\n", array[i],
+ gda_g_type_to_string (coltype), act);*/
+ g_strfreev (sub);
+ }
+ g_strfreev (array);
+ g_hash_table_destroy (colnames);
+ return g_list_reverse (columns);
+}
+
+
+/*
+ * _gdaprov_data_model_ldap_new:
+ * @cnc: an LDAP opened connection
+ * @base_dn: the base DN to search on, or %NULL
+ * @filter: an LDAP filter (for example "(objectClass=*)");
+ * @attributes: the list of attributes to fetch, each in the format <attname>[::<GType>] (+CSV,...)
+ * @scope: the search scope
+ *
+ * Creates a new #GdaDataModel object to extract some LDAP contents
+ *
+ * Returns: a new #GdaDataModel
+ */
+GdaDataModel *
+_gdaprov_data_model_ldap_new (GdaConnection *cnc, const gchar *base_dn, const gchar *filter,
+ const gchar *attributes, GdaLdapSearchScope scope)
+{
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), NULL);
+
+ return (GdaDataModel *) g_object_new (GDA_TYPE_DATA_MODEL_LDAP, "cnc", cnc,
+ "base", base_dn,
+ "filter", filter, "attributes", attributes,
+ "scope", scope,
+ NULL);
+}
+
+static gint
+gda_data_model_ldap_get_n_rows (GdaDataModel *model)
+{
+ GdaDataModelLdap *imodel = (GdaDataModelLdap *) model;
+
+ g_return_val_if_fail (GDA_IS_DATA_MODEL_LDAP (imodel), -1);
+ g_return_val_if_fail (imodel->priv != NULL, -1);
+
+ return -1;
+}
+
+static gint
+gda_data_model_ldap_get_n_columns (GdaDataModel *model)
+{
+ GdaDataModelLdap *imodel;
+ g_return_val_if_fail (GDA_IS_DATA_MODEL_LDAP (model), 0);
+ imodel = GDA_DATA_MODEL_LDAP (model);
+ g_return_val_if_fail (imodel->priv, 0);
+
+ if (imodel->priv->columns)
+ return imodel->priv->n_columns;
+ else
+ return 0;
+}
+
+static GdaColumn *
+gda_data_model_ldap_describe_column (GdaDataModel *model, gint col)
+{
+ GdaDataModelLdap *imodel;
+ g_return_val_if_fail (GDA_IS_DATA_MODEL_LDAP (model), NULL);
+ imodel = GDA_DATA_MODEL_LDAP (model);
+ g_return_val_if_fail (imodel->priv, NULL);
+
+ if (imodel->priv->columns)
+ return g_list_nth_data (imodel->priv->columns, col);
+ else
+ return NULL;
+}
+
+static GdaDataModelAccessFlags
+gda_data_model_ldap_get_access_flags (GdaDataModel *model)
+{
+ GdaDataModelLdap *imodel;
+ GdaDataModelAccessFlags flags;
+
+ g_return_val_if_fail (GDA_IS_DATA_MODEL_LDAP (model), 0);
+ imodel = GDA_DATA_MODEL_LDAP (model);
+ g_return_val_if_fail (imodel->priv, 0);
+
+ flags = GDA_DATA_MODEL_ACCESS_CURSOR_FORWARD;
+
+ return flags;
+}
+
+static gchar *
+csv_quote (const gchar *string)
+{
+ gchar *retval, *ptrd;
+ const gchar *ptrs;
+ retval = g_new (gchar, strlen (string) * 2 + 3);
+ *retval = '"';
+ for (ptrd = retval + 1, ptrs = string; *ptrs; ptrs++) {
+ if (*ptrs == '"') {
+ *ptrd = '"';
+ ptrd++;
+ }
+ *ptrd = *ptrs;
+ ptrd++;
+ }
+ *ptrd = '"';
+ ptrd++;
+ *ptrd = 0;
+ return retval;
+}
+
+static void
+update_iter_from_ldap_row (GdaDataModelLdap *imodel, GdaDataModelIter *iter)
+{
+ gboolean update_model;
+ BerElement* ber;
+ char *attr;
+ GdaHolder *holder;
+ gint j, nb;
+ LdapConnectionData *cdata;
+ GSList *holders_set = NULL;
+ cdata = (LdapConnectionData*) gda_virtual_connection_internal_get_provider_data (GDA_VIRTUAL_CONNECTION (imodel->priv->cnc));
+ g_return_if_fail (cdata);
+
+ g_object_get (G_OBJECT (iter), "update-model", &update_model, NULL);
+ g_object_set (G_OBJECT (iter), "update-model", FALSE, NULL);
+
+ /* column 0 is the DN */
+ holder = GDA_HOLDER (GDA_SET (iter)->holders->data);
+ attr = ldap_get_dn (cdata->handle, imodel->priv->current_exec->ldap_row);
+ if (attr) {
+ gchar *userdn;
+ if (gda_ldap_parse_dn (attr, &userdn)) {
+ gda_holder_set_value_str (holder, NULL, userdn, NULL);
+ g_free (userdn);
+ }
+ else
+ gda_holder_force_invalid (holder);
+ ldap_memfree (attr);
+ }
+ else
+ gda_holder_force_invalid (holder);
+
+ nb = g_slist_length (((GdaSet*) iter)->holders);
+ for (j = 1; j < nb; j++) {
+ holder = (GdaHolder*) (g_slist_nth_data (((GdaSet*) iter)->holders, j));
+ gda_holder_set_value (holder, NULL, NULL);
+ }
+
+ if (imodel->priv->row_mult)
+ goto out;
+
+ for (attr = ldap_first_attribute (cdata->handle, imodel->priv->current_exec->ldap_row, &ber);
+ attr;
+ attr = ldap_next_attribute (cdata->handle, imodel->priv->current_exec->ldap_row, ber)) {
+ BerValue **bvals;
+ gboolean holder_added_to_cm = FALSE;
+
+ holder = gda_set_get_holder ((GdaSet*) iter, attr);
+ if (!holder)
+ continue;
+
+ j = g_slist_index (((GdaSet*) iter)->holders, holder);
+
+ bvals = ldap_get_values_len (cdata->handle,
+ imodel->priv->current_exec->ldap_row, attr);
+ if (bvals) {
+ if (bvals[0] && bvals[1]) {
+ /* multiple values */
+ MultipleValueAction act;
+ act = g_array_index (imodel->priv->column_mv_actions,
+ MultipleValueAction, j-1);
+ switch (act) {
+ case MULTIPLE_VALUE_ACTION_SET_NULL:
+ gda_holder_set_value (holder, NULL, NULL);
+ break;
+ case MULTIPLE_VALUE_ACTION_CSV_STRING:
+ if ((gda_holder_get_g_type (holder) == G_TYPE_STRING)) {
+ GString *string = NULL;
+ gint i;
+ GValue *value;
+ for (i = 0; bvals[i]; i++) {
+ gchar *tmp;
+ if (string)
+ g_string_append_c (string, ',');
+ else
+ string = g_string_new ("");
+ tmp = csv_quote (bvals[i]->bv_val);
+ g_string_append (string, tmp);
+ g_free (tmp);
+ }
+ value = gda_value_new (G_TYPE_STRING);
+ g_value_take_string (value, string->str);
+ g_string_free (string, FALSE);
+ gda_holder_take_value (holder, value, NULL);
+ }
+ else
+ gda_holder_force_invalid (holder);
+ break;
+ case MULTIPLE_VALUE_ACTION_MULTIPLY: {
+ ColumnMultiplier *cm;
+ if (! imodel->priv->row_mult) {
+ imodel->priv->row_mult = row_multiplier_new ();
+ /* create @cm for the previous columns */
+ GSList *list;
+ for (list = holders_set; list; list = list->next) {
+ GdaHolder *ch;
+ ch = (GdaHolder*) list->data;
+ cm = column_multiplier_new (ch,
+ gda_holder_get_value (ch));
+ g_array_append_val (imodel->priv->row_mult->cms, cm);
+ }
+ }
+ /* add new @cm */
+ cm = column_multiplier_new (holder, NULL);
+ gint i;
+ for (i = 0; bvals[i]; i++) {
+ GValue *value;
+ value = gda_ldap_attr_value_to_g_value (cdata,
+ gda_holder_get_g_type (holder),
+ bvals[i]);
+ g_array_append_val (cm->values, value); /* value can be %NULL */
+ }
+ g_array_append_val (imodel->priv->row_mult->cms, cm);
+ holder_added_to_cm = TRUE;
+ break;
+ }
+ case MULTIPLE_VALUE_ACTION_FIRST:
+ if ((gda_holder_get_g_type (holder) == G_TYPE_STRING)) {
+ GValue *value;
+ value = gda_value_new (G_TYPE_STRING);
+ g_value_set_string (value, bvals[0]->bv_val);
+ gda_holder_take_value (holder, value, NULL);
+ }
+ else
+ gda_holder_force_invalid (holder);
+ break;
+ case MULTIPLE_VALUE_ACTION_CONCAT:
+ if ((gda_holder_get_g_type (holder) == G_TYPE_STRING)) {
+ GString *string = NULL;
+ gint i;
+ GValue *value;
+ for (i = 0; bvals[i]; i++) {
+ if (string)
+ g_string_append_c (string, '\n');
+ else
+ string = g_string_new ("");
+ g_string_append (string, bvals[i]->bv_val);
+ }
+ value = gda_value_new (G_TYPE_STRING);
+ g_value_take_string (value, string->str);
+ g_string_free (string, FALSE);
+ gda_holder_take_value (holder, value, NULL);
+ }
+ else
+ gda_holder_force_invalid (holder);
+ break;
+ case MULTIPLE_VALUE_ACTION_SET_INVALID:
+ default:
+ gda_holder_force_invalid (holder);
+ break;
+ }
+ }
+ else if (bvals[0]) {
+ /* convert string to the correct type */
+ GValue *value;
+ value = gda_ldap_attr_value_to_g_value (cdata, gda_holder_get_g_type (holder),
+ bvals[0]);
+ if (value)
+ gda_holder_take_value (holder, value, NULL);
+ else
+ gda_holder_force_invalid (holder);
+ }
+ else
+ gda_holder_set_value (holder, NULL, NULL);
+ ldap_value_free_len (bvals);
+ }
+ else
+ gda_holder_set_value (holder, NULL, NULL);
+ ldap_memfree (attr);
+ holders_set = g_slist_prepend (holders_set, holder);
+ if (imodel->priv->row_mult && !holder_added_to_cm) {
+ ColumnMultiplier *cm;
+ cm = column_multiplier_new (holder, gda_holder_get_value (holder));
+ g_array_append_val (imodel->priv->row_mult->cms, cm);
+ }
+ }
+ if (holders_set)
+ g_slist_free (holders_set);
+
+ if (ber)
+ ber_free (ber, 0);
+
+ out:
+ if (imodel->priv->row_mult)
+ row_multiplier_set_holders (imodel->priv->row_mult);
+
+ if (gda_data_model_iter_is_valid (iter)) {
+ imodel->priv->iter_row ++;
+ if ((imodel->priv->iter_row == imodel->priv->n_rows - 1) && imodel->priv->truncated) {
+ GError *e;
+ g_set_error (&e, GDA_DATA_MODEL_ERROR,
+ GDA_DATA_MODEL_TRUNCATED_ERROR,
+ _("Truncated result because LDAP server limit encountered"));
+ add_exception (imodel, e);
+ }
+ }
+ else
+ imodel->priv->iter_row = 0;
+
+ g_object_set (G_OBJECT (iter), "current-row", imodel->priv->iter_row,
+ "update-model", update_model, NULL);
+}
+
+/*
+ * Add an exception to @model
+ * WARNING: steals @e
+ */
+static void
+add_exception (GdaDataModelLdap *model, GError *e)
+{
+ if (!model->priv->exceptions)
+ model->priv->exceptions = g_array_new (TRUE, FALSE, sizeof (GError*));
+ g_array_append_val (model->priv->exceptions, e);
+}
+
+/*
+ * Execute model->priv->current_exec and either:
+ * - sets model->priv->current_exec->ldap_msg, or
+ * - create some children and execute one, or
+ * - sets model->priv->current_exec->ldap_msg to %NULL if an error occurred
+ *
+ * In any case model->priv->current_exec->executed is set to %TRUE and should be %FALSE when entering
+ * the function (ie. for any LdapConnectionData this function has to be called at most once)
+ */
+static void
+execute_ldap_search (GdaDataModelLdap *model)
+{
+ LDAPMessage *msg = NULL;
+ int lscope, res = 0;
+ LdapConnectionData *cdata;
+ cdata = (LdapConnectionData*) gda_virtual_connection_internal_get_provider_data (GDA_VIRTUAL_CONNECTION (model->priv->cnc));
+ g_return_if_fail (cdata);
+
+ g_assert (model->priv->current_exec);
+ g_assert (! model->priv->current_exec->executed);
+
+ switch (model->priv->current_exec->scope) {
+ default:
+ case GDA_LDAP_SEARCH_BASE:
+ lscope = LDAP_SCOPE_BASE;
+ break;
+ case GDA_LDAP_SEARCH_ONELEVEL:
+ lscope = LDAP_SCOPE_ONELEVEL;
+ break;
+ case GDA_LDAP_SEARCH_SUBTREE:
+ lscope = LDAP_SCOPE_SUBTREE;
+ break;
+ }
+
+#ifdef GDA_DEBUG_SUBSEARCHES
+ if (model->priv->scope == GDA_LDAP_SEARCH_SUBTREE) {
+ g_print ("Model %p model->priv->top_exec:\n", model);
+ ldap_part_dump (model->priv->top_exec);
+ }
+#endif
+ retry:
+#ifdef GDA_DEBUG_SUBSEARCHES_FORCE
+ /* force sub searches for 2 levels */
+ static gint sims = 10;
+ if ((sims > 0) &&
+ (model->priv->scope == GDA_LDAP_SEARCH_SUBTREE) &&
+ (! model->priv->current_exec->parent || ! model->priv->current_exec->parent->parent)) {
+ g_print ("Simulating LDAP_ADMINLIMIT_EXCEEDED\n");
+ res = LDAP_ADMINLIMIT_EXCEEDED;
+ sims --;
+ }
+ if (res == 0)
+#endif
+
+ res = ldap_search_ext_s (cdata->handle, model->priv->current_exec->base_dn, lscope,
+ model->priv->filter,
+ (char**) model->priv->attributes->data, 0,
+ NULL, NULL, NULL, -1,
+ &msg);
+ model->priv->current_exec->executed = TRUE;
+
+ switch (res) {
+ case LDAP_SUCCESS:
+ case LDAP_NO_SUCH_OBJECT:
+ /* all Ok */
+ model->priv->current_exec->ldap_msg = msg;
+ model->priv->current_exec->nb_entries = ldap_count_entries (cdata->handle, msg);
+#ifdef GDA_DEBUG_SUBSEARCHES
+ g_print ("model->priv->current_exec->nb_entries = %d\n",
+ model->priv->current_exec->nb_entries);
+#endif
+ break;
+
+ case LDAP_ADMINLIMIT_EXCEEDED:
+ case LDAP_SIZELIMIT_EXCEEDED:
+ case LDAP_TIMELIMIT_EXCEEDED: {
+#ifdef GDA_DEBUG_SUBSEARCHES
+ g_print ("LIMIT_EXCEEDED!\n");
+#endif
+ gboolean handled = FALSE;
+ if (model->priv->scope == GDA_LDAP_SEARCH_SUBTREE) {
+ gboolean split_error;
+ if (ldap_part_split (model->priv->current_exec, model, &split_error)) {
+ /* create some children to re-run the search */
+ if (msg)
+ ldap_msgfree (msg);
+ model->priv->current_exec = LDAP_PART (model->priv->current_exec->children->data);
+ execute_ldap_search (model);
+ handled = TRUE;
+ }
+ else if (!split_error) {
+ LdapPart *next;
+ next = ldap_part_next (model->priv->current_exec, FALSE);
+ if (next) {
+ model->priv->current_exec = next;
+ execute_ldap_search (model);
+ handled = TRUE;
+ }
+ }
+ }
+ if (!handled) {
+ /* truncated output */
+#ifdef GDA_DEBUG_SUBSEARCHES
+ g_print ("Output truncated!\n");
+#endif
+ model->priv->truncated = TRUE;
+ model->priv->current_exec->ldap_msg = msg;
+ model->priv->current_exec->nb_entries = ldap_count_entries (cdata->handle, msg);
+ }
+ break;
+ }
+ case LDAP_SERVER_DOWN: {
+ gint i;
+ for (i = 0; i < 5; i++) {
+ if (gda_ldap_silently_rebind (cdata))
+ goto retry;
+ g_usleep (G_USEC_PER_SEC * 2);
+ }
+ }
+ default: {
+ /* error */
+ GError *e = NULL;
+ int ldap_errno;
+ ldap_get_option (cdata->handle, LDAP_OPT_ERROR_NUMBER, &ldap_errno);
+ g_set_error (&e, GDA_DATA_MODEL_ERROR, GDA_DATA_MODEL_OTHER_ERROR,
+ "%s", ldap_err2string(ldap_errno));
+ add_exception (model, e);
+ return;
+ }
+ }
+
+ if (model->priv->truncated) {
+ /* compute totel number of rows now that we know it */
+ if (model->priv->top_exec->ldap_msg)
+ model->priv->n_rows = model->priv->current_exec->nb_entries;
+ else {
+ LdapPart *iter;
+ model->priv->n_rows = 0;
+ for (iter = model->priv->top_exec; iter; iter = ldap_part_next (iter, TRUE))
+ model->priv->n_rows += iter->nb_entries;
+ }
+ }
+#ifdef GDA_DEBUG_NO
+ gint tmpnb = 0;
+ if (model->priv->top_exec->ldap_msg)
+ tmpnb = model->priv->current_exec->nb_entries;
+ else {
+ LdapPart *iter;
+ for (iter = model->priv->top_exec; iter; iter = ldap_part_next (iter, TRUE))
+ tmpnb += iter->nb_entries;
+ }
+ g_print ("So far found %d\n", tmpnb);
+#endif
+}
+
+static gboolean
+gda_data_model_ldap_iter_next (GdaDataModel *model, GdaDataModelIter *iter)
+{
+ GdaDataModelLdap *imodel;
+ LdapConnectionData *cdata;
+ LdapPart *cpart;
+
+ g_return_val_if_fail (GDA_IS_DATA_MODEL_LDAP (model), FALSE);
+ g_return_val_if_fail (GDA_IS_DATA_MODEL_ITER (iter), FALSE);
+ imodel = GDA_DATA_MODEL_LDAP (model);
+ g_return_val_if_fail (imodel->priv, FALSE);
+
+ if (! imodel->priv->cnc) {
+ /* error */
+ return FALSE;
+ }
+
+ cdata = (LdapConnectionData*) gda_virtual_connection_internal_get_provider_data (GDA_VIRTUAL_CONNECTION (imodel->priv->cnc));
+ if (!cdata) {
+ /* error */
+ return FALSE;
+ }
+
+ /* initialize LDAP search if necessary */
+ if (! imodel->priv->base_dn)
+ imodel->priv->base_dn = g_strdup (cdata->base_dn);
+ if (! imodel->priv->attributes)
+ imodel->priv->attributes = g_array_new (TRUE, FALSE, sizeof (gchar*));
+ if (! imodel->priv->top_exec) {
+ imodel->priv->top_exec = ldap_part_new (NULL, imodel->priv->base_dn, imodel->priv->scope);
+ imodel->priv->current_exec = imodel->priv->top_exec;
+ }
+
+ while (imodel->priv->current_exec) {
+ cpart = imodel->priv->current_exec;
+ if (! cpart->executed)
+ execute_ldap_search (imodel);
+ cpart = imodel->priv->current_exec;
+ if (! cpart->ldap_msg) {
+ /* error somewhere */
+ return FALSE;
+ }
+
+ if (! cpart->ldap_row)
+ /* not yet on 1st row */
+ cpart->ldap_row = ldap_first_entry (cdata->handle, cpart->ldap_msg);
+ else {
+ if (imodel->priv->row_mult) {
+ if (! row_multiplier_index_next (imodel->priv->row_mult)) {
+ row_multiplier_free (imodel->priv->row_mult);
+ imodel->priv->row_mult = NULL;
+ }
+ }
+ if (! imodel->priv->row_mult) {
+ /* move to the next row */
+ cpart->ldap_row = ldap_next_entry (cdata->handle, cpart->ldap_row);
+ }
+ }
+
+ if (cpart->ldap_row) {
+ update_iter_from_ldap_row (imodel, iter);
+ break;
+ }
+ else
+ /* nothing more for this part, switch to the next one */
+ imodel->priv->current_exec = ldap_part_next (cpart, FALSE);
+ }
+
+ if (!imodel->priv->current_exec) {
+ /* execution is over */
+ g_signal_emit_by_name (iter, "end-of-data");
+ g_object_set (G_OBJECT (iter), "current-row", -1, NULL);
+ if (imodel->priv->truncated) {
+ GError *e;
+ g_set_error (&e, GDA_DATA_MODEL_ERROR,
+ GDA_DATA_MODEL_TRUNCATED_ERROR,
+ _("Truncated result because LDAP server limit encountered"));
+ add_exception (imodel, e);
+ }
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static GdaValueAttribute
+gda_data_model_ldap_get_attributes_at (GdaDataModel *model, gint col, G_GNUC_UNUSED gint row)
+{
+ GdaDataModelLdap *imodel;
+ GdaValueAttribute flags;
+ GdaColumn *column;
+ g_return_val_if_fail (GDA_IS_DATA_MODEL_LDAP (model), 0);
+ imodel = GDA_DATA_MODEL_LDAP (model);
+
+ if ((col < 0) || (col > imodel->priv->n_columns)) {
+ /* error */
+ /*
+ gchar *tmp;
+ tmp = g_strdup_printf (_("Column %d out of range (0-%d)"), col, imodel->priv->n_columns - 1);
+ add_error (imodel, tmp);
+ g_free (tmp);
+ */
+ return 0;
+ }
+
+ flags = GDA_VALUE_ATTR_NO_MODIF;
+ column = g_list_nth_data (imodel->priv->columns, col);
+ if (gda_column_get_allow_null (column))
+ flags |= GDA_VALUE_ATTR_CAN_BE_NULL;
+
+ return flags;
+}
+
+static GError **
+gda_data_model_ldap_get_exceptions (GdaDataModel *model)
+{
+ GdaDataModelLdap *imodel;
+ g_return_val_if_fail (GDA_IS_DATA_MODEL_LDAP (model), NULL);
+ imodel = GDA_DATA_MODEL_LDAP (model);
+ if (imodel->priv->exceptions)
+ return (GError**) imodel->priv->exceptions->data;
+ else
+ return NULL;
+}
+
+/*
+ * Parts manipulations
+ */
+static LdapPart *
+ldap_part_new (LdapPart *parent, const gchar *base_dn, GdaLdapSearchScope scope)
+{
+ LdapPart *part;
+ if (!base_dn || !*base_dn)
+ return NULL;
+ part = g_new0 (LdapPart, 1);
+ part->base_dn = g_strdup (base_dn);
+ part->scope = scope;
+ part->ldap_msg = NULL;
+ part->ldap_row = NULL;
+ part->children = NULL;
+ part->parent = parent;
+ return part;
+}
+
+static void
+ldap_part_free (LdapPart *part)
+{
+ g_assert (part);
+ g_free (part->base_dn);
+ if (part->children) {
+ g_slist_foreach (part->children, (GFunc) ldap_part_free, NULL);
+ g_slist_free (part->children);
+ }
+ if (part->ldap_msg)
+ ldap_msgfree (part->ldap_msg);
+ g_free (part);
+}
+
+/*
+ * Go to the next part in the tree
+ */
+static LdapPart *
+ldap_part_next (LdapPart *part, gboolean executed)
+{
+ LdapPart *parent, *retval = NULL;
+ if (part->children)
+ retval = LDAP_PART (part->children->data);
+ else {
+ LdapPart *tmp;
+ tmp = part;
+ for (parent = tmp->parent; parent; parent = tmp->parent) {
+ gint i;
+ i = g_slist_index (parent->children, tmp);
+ tmp = g_slist_nth_data (parent->children, i+1);
+ if (tmp) {
+ retval = tmp;
+ break;
+ }
+ else
+ tmp = parent;
+ }
+ }
+
+ if (retval) {
+ if (executed && !retval->executed)
+ return ldap_part_next (retval, executed);
+ else if (!executed && retval->executed)
+ return ldap_part_next (retval, executed);
+ }
+
+ if (retval == part) {
+ TO_IMPLEMENT;
+ }
+ g_assert (retval != part);
+ return retval;
+}
+
+/*
+ * Creates sub parts of @part, one for each immediate child pf @part->base_dn
+ *
+ * Returns: %TRUE if part->children is not %NULL
+ */
+static gboolean
+ldap_part_split (LdapPart *part, GdaDataModelLdap *model, gboolean *out_error)
+{
+ /* fetch all children entries of @part, and build a child part
+ * for each of them */
+ GdaDataModel *mparts;
+ GdaDataModelIter *iter;
+
+ if (out_error)
+ *out_error = FALSE;
+ g_assert (!part->children);
+ mparts = _gdaprov_data_model_ldap_new (model->priv->cnc, part->base_dn, NULL, NULL,
+ GDA_LDAP_SEARCH_ONELEVEL);
+ if (!mparts) {
+ if (out_error)
+ *out_error = TRUE;
+ return FALSE;
+ }
+
+ if (part->scope == GDA_LDAP_SEARCH_SUBTREE) {
+ /* create a part for the node itself */
+ LdapPart *sub = NULL;
+ sub = ldap_part_new (part, part->base_dn, GDA_LDAP_SEARCH_BASE);
+ g_assert (sub);
+ part->children = g_slist_prepend (part->children, sub);
+ }
+
+ iter = gda_data_model_create_iter (mparts);
+ for (; gda_data_model_iter_move_next (iter); ) {
+ const GValue *cvalue;
+ LdapPart *sub = NULL;
+ cvalue = gda_data_model_iter_get_value_at (iter, 0);
+ if (cvalue) {
+ gchar *tmp;
+ tmp = gda_value_stringify (cvalue);
+ sub = ldap_part_new (part, tmp, part->scope);
+ if (sub)
+ part->children = g_slist_prepend (part->children, sub);
+ g_free (tmp);
+ }
+ if (!sub) {
+ /* error */
+ g_slist_foreach (part->children, (GFunc) ldap_part_free, NULL);
+ g_slist_free (part->children);
+ part->children = NULL;
+ break;
+ }
+ }
+ g_object_unref (mparts);
+
+ return part->children ? TRUE : FALSE;
+}
+
+#ifdef GDA_DEBUG_SUBSEARCHES
+static void
+ldap_part_dump_to_string (LdapPart *part, GString *string, gint index, gboolean recurs)
+{
+ gchar *indent;
+ indent = g_new (gchar, index * 4 + 1);
+ memset (indent, ' ', sizeof (gchar) * index * 4);
+ indent [index * 4] = 0;
+ g_string_append (string, indent);
+ g_free (indent);
+
+ g_string_append_printf (string, "part[%p] scope[%d] base[%s] %s nb_entries[%d]\n", part,
+ part->scope, part->base_dn,
+ part->executed ? "EXEC" : "non exec", part->nb_entries);
+
+ if (recurs && part->children) {
+ GSList *list;
+ for (list = part->children; list; list = list->next)
+ ldap_part_dump_to_string (LDAP_PART (list->data), string, index+1, recurs);
+ }
+}
+
+static void
+ldap_part_dump (LdapPart *part)
+{
+ GString *string;
+ string = g_string_new ("");
+ ldap_part_dump_to_string (part, string, 0, TRUE);
+ g_print ("%s\n", string->str);
+ g_string_free (string, TRUE);
+}
+#endif
+
+
+/*
+ * Row & column multiplier
+ */
+
+static ColumnMultiplier *
+column_multiplier_new (GdaHolder *holder, const GValue *value)
+{
+ ColumnMultiplier *cm;
+ cm = g_new0 (ColumnMultiplier, 1);
+ cm->holder = g_object_ref (holder);
+ cm->index = 0;
+ cm->values = g_array_new (FALSE, FALSE, sizeof (GValue*));
+ if (value) {
+ GValue *copy;
+ copy = gda_value_copy (value);
+ g_array_append_val (cm->values, copy);
+ }
+ return cm;
+}
+
+static RowMultiplier *
+row_multiplier_new (void)
+{
+ RowMultiplier *rm;
+ rm = g_new0 (RowMultiplier, 1);
+ rm->cms = g_array_new (FALSE, FALSE, sizeof (ColumnMultiplier*));
+ return rm;
+}
+
+static void
+row_multiplier_set_holders (RowMultiplier *rm)
+{
+ gint i;
+ for (i = 0; i < rm->cms->len; i++) {
+ ColumnMultiplier *cm;
+ GValue *value;
+ cm = g_array_index (rm->cms, ColumnMultiplier*, i);
+ value = g_array_index (cm->values, GValue *, cm->index);
+ if (value)
+ gda_holder_set_value (cm->holder, value, NULL);
+ else
+ gda_holder_force_invalid (cm->holder);
+ }
+}
+
+static void
+row_multiplier_free (RowMultiplier *rm)
+{
+ gint i;
+ for (i = 0; i < rm->cms->len; i++) {
+ ColumnMultiplier *cm;
+ cm = g_array_index (rm->cms, ColumnMultiplier*, i);
+ gint j;
+ for (j = 0; j < cm->values->len; j++) {
+ GValue *value;
+ value = g_array_index (cm->values, GValue *, j);
+ if (value)
+ gda_value_free (value);
+ }
+ g_array_free (cm->values, TRUE);
+ g_object_unref (cm->holder);
+ g_free (cm);
+ }
+ g_array_free (rm->cms, TRUE);
+ g_free (rm);
+}
+
+/*
+ * move indexes one step forward
+ */
+static gboolean
+row_multiplier_index_next (RowMultiplier *rm)
+{
+ gint i;
+#ifdef GDA_DEBUG_NO
+ g_print ("RM %p before:\n", rm);
+ for (i = 0; i < rm->cms->len; i++) {
+ ColumnMultiplier *cm;
+ cm = g_array_index (rm->cms, ColumnMultiplier*, i);
+ g_print (" %d ", cm->index);
+ }
+ g_print ("\n");
+#endif
+
+ for (i = 0; ; i++) {
+ ColumnMultiplier *cm;
+ if (i == rm->cms->len) {
+#ifdef GDA_DEBUG_NO
+ g_print ("RM %p [FALSE]:\n", rm);
+ for (i = 0; i < rm->cms->len; i++) {
+ ColumnMultiplier *cm;
+ cm = g_array_index (rm->cms, ColumnMultiplier*, i);
+ g_print (" %d ", cm->index);
+ }
+ g_print ("\n");
+#endif
+ return FALSE;
+ }
+
+ cm = g_array_index (rm->cms, ColumnMultiplier*, i);
+ if (cm->index < (cm->values->len - 1)) {
+ cm->index ++;
+#ifdef GDA_DEBUG_NO
+ g_print ("RM %p [TRUE]:\n", rm);
+ for (i = 0; i < rm->cms->len; i++) {
+ ColumnMultiplier *cm;
+ cm = g_array_index (rm->cms, ColumnMultiplier*, i);
+ g_print (" %d ", cm->index);
+ }
+ g_print ("\n");
+#endif
+ return TRUE;
+ }
+ else {
+ gint j;
+ for (j = 0; j < i; j++) {
+ cm = g_array_index (rm->cms, ColumnMultiplier*, j);
+ cm->index = 0;
+ }
+ }
+ }
+}
diff --git a/libgda/sqlite/virtual/libgda-virtual.h b/providers/ldap/gdaprov-data-model-ldap.h
similarity index 60%
rename from libgda/sqlite/virtual/libgda-virtual.h
rename to providers/ldap/gdaprov-data-model-ldap.h
index 2986ccf..bba6074 100644
--- a/libgda/sqlite/virtual/libgda-virtual.h
+++ b/providers/ldap/gdaprov-data-model-ldap.h
@@ -1,5 +1,5 @@
-/* GDA library
- * Copyright (C) 2007 The GNOME Foundation.
+/*
+ * Copyright (C) 2011 The GNOME Foundation.
*
* AUTHORS:
* Vivien Malerba <malerba gnome-db org>
@@ -20,15 +20,19 @@
* Boston, MA 02111-1307, USA.
*/
-#ifndef __LIBGDA_VIRTUAL_H__
-#define __LIBGDA_VIRTUAL_H__
+#ifndef __GDA_PROXY_DATA_MODEL_LDAP_H__
+#define __GDA_PROXY_DATA_MODEL_LDAP_H__
-#include <virtual/gda-virtual-provider.h>
-#include <virtual/gda-vprovider-data-model.h>
-#include <virtual/gda-vprovider-hub.h>
+#include <libgda/gda-data-model-ldap.h>
-#include <virtual/gda-virtual-connection.h>
-#include <virtual/gda-vconnection-data-model.h>
-#include <virtual/gda-vconnection-hub.h>
+G_BEGIN_DECLS
+
+GType gdaprov_data_model_ldap_get_type (void) G_GNUC_CONST;
+GdaDataModel *_gdaprov_data_model_ldap_new (GdaConnection *cnc,
+ const gchar *base_dn, const gchar *filter,
+ const gchar *attributes, GdaLdapSearchScope scope);
+GList *gdaprov_data_model_ldap_compute_columns (GdaConnection *cnc, const gchar *attributes);
+
+G_END_DECLS
#endif
diff --git a/providers/ldap/ldap_specs_auth.xml.in b/providers/ldap/ldap_specs_auth.xml.in
new file mode 100644
index 0000000..60ce8f5
--- /dev/null
+++ b/providers/ldap/ldap_specs_auth.xml.in
@@ -0,0 +1,7 @@
+<?xml version="1.0"?>
+<data-set-spec>
+ <parameters>
+ <parameter id="USERNAME" _name="Username" _descr="User name" gdatype="gchararray" nullok="TRUE"/>
+ <parameter id="PASSWORD" _name="Password" _descr="Password" gdatype="gchararray" nullok="TRUE" plugin="string:HIDDEN=true"/>
+ </parameters>
+</data-set-spec>
diff --git a/providers/ldap/ldap_specs_dsn.xml.in b/providers/ldap/ldap_specs_dsn.xml.in
new file mode 100644
index 0000000..9e2ca99
--- /dev/null
+++ b/providers/ldap/ldap_specs_dsn.xml.in
@@ -0,0 +1,12 @@
+<?xml version="1.0"?>
+<data-set-spec>
+ <parameters>
+ <parameter id="DB_NAME" _name="Base name" _descr="Base distinguished name, starting point for the searches" gdatype="gchararray" nullok="FALSE"/>
+ <parameter id="HOST" _name="Database server" _descr="Host on which the LDAP server is running" gdatype="gchararray"/>
+ <parameter id="PORT" _name="Port" _descr="Database server port (leave this field empty to use the default port)" gdatype="gint"/>
+ <parameter id="USE_SSL" _name="Require SSL" _descr="Whether or not to use SSL to establish the connection" gdatype="gboolean"/>
+ <parameter id="USE_CACHE" _name="Cache server data" _descr="Use a cache to store some static server data (the cached files are in the user's cache directory), default is TRUE" gdatype="gboolean">
+ <gda_value>TRUE</gda_value>
+ </parameter>
+ </parameters>
+</data-set-spec>
diff --git a/providers/ldap/libgda-ldap-5.0.pc.in b/providers/ldap/libgda-ldap-5.0.pc.in
new file mode 100644
index 0000000..21c7a96
--- /dev/null
+++ b/providers/ldap/libgda-ldap-5.0.pc.in
@@ -0,0 +1,9 @@
+prefix= prefix@
+exec_prefix= exec_prefix@
+libdir= libdir@
+includedir= includedir@
+
+Name: libgda-ldap- GDA_ABI_MAJOR_VERSION@ GDA_ABI_MINOR_VERSION@
+Description: GDA Ldap provider
+Requires: libgda- GDA_ABI_MAJOR_VERSION@ GDA_ABI_MINOR_VERSION@
+Version: @VERSION@
diff --git a/providers/ldap/libmain.c b/providers/ldap/libmain.c
new file mode 100644
index 0000000..82ac438
--- /dev/null
+++ b/providers/ldap/libmain.c
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2011 The GNOME Foundation
+ *
+ * AUTHORS:
+ * Vivien Malerba <malerba gnome-db org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <glib/gi18n-lib.h>
+#include <gmodule.h>
+#include <libgda/gda-config.h>
+#include <libgda/gda-server-provider-extra.h>
+#include <libgda/binreloc/gda-binreloc.h>
+#include "gda-ldap.h"
+#include "gda-ldap-provider.h"
+
+static gchar *module_path = NULL;
+const gchar *plugin_get_name (void);
+const gchar *plugin_get_description (void);
+gchar *plugin_get_dsn_spec (void);
+GdaServerProvider *plugin_create_provider (void);
+
+/*
+ * Functions executed when calling g_module_open() and g_module_close()
+ */
+const gchar *
+g_module_check_init (G_GNUC_UNUSED GModule *module)
+{
+ return NULL;
+}
+
+void
+g_module_unload (G_GNUC_UNUSED GModule *module)
+{
+ g_free (module_path);
+ module_path = NULL;
+}
+
+/*
+ * Normal plugin functions
+ */
+void
+plugin_init (const gchar *real_path)
+{
+ if (real_path)
+ module_path = g_strdup (real_path);
+}
+
+const gchar *
+plugin_get_name (void)
+{
+ return LDAP_PROVIDER_NAME;
+}
+
+const gchar *
+plugin_get_description (void)
+{
+ return _("Provider for database where tables are based on data contained in an LDAP directory");
+}
+
+gchar *
+plugin_get_dsn_spec (void)
+{
+ gchar *ret, *dir;
+
+ dir = gda_gbr_get_file_path (GDA_DATA_DIR, LIBGDA_ABI_NAME, NULL);
+ ret = gda_server_provider_load_file_contents (module_path, dir, "ldap_specs_dsn.xml");
+ g_free (dir);
+ return ret;
+}
+
+gchar *
+plugin_get_auth_spec (void)
+{
+ gchar *ret, *dir;
+
+ dir = gda_gbr_get_file_path (GDA_DATA_DIR, LIBGDA_ABI_NAME, NULL);
+ ret = gda_server_provider_load_file_contents (module_path, dir, "ldap_specs_auth.xml");
+ g_free (dir);
+ return ret;
+}
+
+GdaServerProvider *
+plugin_create_provider (void)
+{
+ GdaServerProvider *prov;
+
+ prov = (GdaServerProvider *) g_object_new (GDA_TYPE_LDAP_PROVIDER, NULL);
+ g_object_set_data ((GObject *) prov, "GDA_PROVIDER_DIR", module_path);
+ return prov;
+}
diff --git a/samples/LdapBrowser/README b/samples/LdapBrowser/README
new file mode 100644
index 0000000..2149c49
--- /dev/null
+++ b/samples/LdapBrowser/README
@@ -0,0 +1,22 @@
+LDAP browser demo
+=================
+
+Description:
+------------
+
+The example in this directory is a very simple LDAP tree browser, illustrating the usage of
+an LDAP connection and the GdaTree and GdaTreeMgrLdap objects.
+
+Compiling and running:
+----------------------
+
+To compile (make sure Libgda is installed prior to this):
+> make
+
+and to run:
+> ./ldap-browser [-h <server>] [-u <username>] [-p <password>] [-b <base>] [-s]
+
+Results:
+--------
+Opens a window with the LDAP entries hierarchy, showing only the RDN of each entry. Selecting
+an entry displays its RND and complete DN in the standard output stream.
\ No newline at end of file
diff --git a/samples/LdapBrowser/ldap-browser.c b/samples/LdapBrowser/ldap-browser.c
new file mode 100644
index 0000000..6296092
--- /dev/null
+++ b/samples/LdapBrowser/ldap-browser.c
@@ -0,0 +1,276 @@
+#include <gtk/gtk.h>
+#include <string.h>
+#include <libgda-ui/libgda-ui.h>
+#include <libgda/libgda.h>
+#include <libgda/virtual/gda-ldap-connection.h>
+
+gchar *host = NULL;
+gchar *base = NULL;
+gchar *user = NULL;
+gchar *password = NULL;
+gboolean usessl = FALSE;
+
+static GOptionEntry entries[] = {
+ { "server", 'h', 0, G_OPTION_ARG_STRING, &host, "Server", NULL},
+ { "basename", 'b', 0, G_OPTION_ARG_STRING, &base, "Base name", NULL},
+ { "user", 'u', 0, G_OPTION_ARG_STRING, &user, "User", NULL},
+ { "password", 'p', 0, G_OPTION_ARG_STRING, &password, "Password", NULL},
+ { "usessl", 's', 0, G_OPTION_ARG_NONE, &usessl, "Use SSL", NULL},
+ { NULL }
+};
+
+
+static GdaConnection *
+open_ldap_connection ()
+{
+ GString *cncstring = NULL;
+ gchar *enc;
+ GdaConnection *cnc;
+ GError *error = NULL;
+
+ if (host) {
+ cncstring = g_string_new ("");
+ enc = gda_rfc1738_encode (host);
+ g_string_append_printf (cncstring, "HOST=%s", enc);
+ g_free (enc);
+ }
+
+ if (base) {
+ if (cncstring)
+ g_string_append_c (cncstring, ';');
+ else
+ cncstring = g_string_new ("");
+ enc = gda_rfc1738_encode (base);
+ g_string_append_printf (cncstring, "DB_NAME=%s", enc);
+ g_free (enc);
+ }
+
+ if (user) {
+ if (cncstring)
+ g_string_append_c (cncstring, ';');
+ else
+ cncstring = g_string_new ("");
+ enc = gda_rfc1738_encode (user);
+ g_string_append_printf (cncstring, "USERNAME=%s", enc);
+ g_free (enc);
+ }
+
+ if (password) {
+ if (cncstring)
+ g_string_append_c (cncstring, ';');
+ else
+ cncstring = g_string_new ("");
+ enc = gda_rfc1738_encode (password);
+ g_string_append_printf (cncstring, "PASSWORD=%s", enc);
+ g_free (enc);
+ }
+
+ if (usessl) {
+ if (cncstring)
+ g_string_append_c (cncstring, ';');
+ else
+ cncstring = g_string_new ("");
+ g_string_append (cncstring, "USER_SSL=TRUE");
+ }
+
+ if (! cncstring) {
+ g_print ("No connection specified!\n");
+ exit (1);
+ }
+
+ g_print ("Using connection string: %s\n", cncstring->str);
+ cnc = gda_connection_open_from_string ("Ldap", cncstring->str,
+ NULL,
+ GDA_CONNECTION_OPTIONS_NONE, &error);
+ if (!cnc) {
+ g_print ("Error opening connection (cncstring=[%s]): %s\n",
+ cncstring->str,
+ error && error->message ? error->message : "No detail");
+ exit (1);
+ }
+ g_string_free (cncstring, TRUE);
+
+ return cnc;
+}
+
+typedef struct {
+ GtkTreeView *tview;
+ GdauiTreeStore *store;
+ GdaTree *tree;
+ GdaTreeNode *node;
+} IdleData;
+
+static void
+idle_data_free (IdleData *data)
+{
+ g_object_unref (data->tview);
+ g_object_unref (data->store);
+ g_object_unref (data->tree);
+ g_object_unref (data->node);
+ g_free (data);
+}
+
+static gboolean ldap_update_tree_part (IdleData *data);
+static gboolean
+test_expand_row_cb (GtkTreeView *tree_view, GtkTreeIter *iter,
+ G_GNUC_UNUSED GtkTreePath *path, GdaTree *tree)
+{
+ GdaTreeNode *node;
+ GtkTreeModel *store;
+
+ store = gtk_tree_view_get_model (tree_view);
+ node = gdaui_tree_store_get_node (GDAUI_TREE_STORE (store), iter);
+ if (gda_tree_node_get_child_index (node, 0))
+ return FALSE;
+
+ const GValue *cv;
+ cv = gda_tree_node_get_node_attribute (node,
+ GDA_ATTRIBUTE_TREE_NODE_UNKNOWN_CHILDREN);
+ if (cv && (G_VALUE_TYPE (cv) == G_TYPE_BOOLEAN) &&
+ g_value_get_boolean (cv)) {
+ IdleData *data;
+ data = g_new (IdleData, 1);
+ data->tview = g_object_ref (G_OBJECT (tree_view));
+ data->store = g_object_ref (G_OBJECT (store));
+ data->tree = g_object_ref (G_OBJECT (tree));
+ data->node = g_object_ref (G_OBJECT (node));
+
+ g_idle_add_full (G_PRIORITY_HIGH_IDLE, (GSourceFunc) ldap_update_tree_part,
+ data, (GDestroyNotify) idle_data_free);
+ }
+ return TRUE;
+}
+
+static gboolean
+ldap_update_tree_part (IdleData *data)
+{
+ gda_tree_update_children (data->tree, data->node, NULL);
+
+ GtkTreeIter iter;
+ if (gdaui_tree_store_get_iter (data->store, &iter, data->node) &&
+ gda_tree_node_get_child_index (data->node, 0)) {
+ GtkTreePath *path;
+ path = gtk_tree_model_get_path (GTK_TREE_MODEL (data->store), &iter);
+ g_signal_handlers_block_by_func (data->tview,
+ G_CALLBACK (test_expand_row_cb), data->tree);
+ gtk_tree_view_expand_row (data->tview, path, FALSE);
+ g_signal_handlers_unblock_by_func (data->tview,
+ G_CALLBACK (test_expand_row_cb), data->tree);
+ gtk_tree_path_free (path);
+ }
+
+ return FALSE;
+}
+
+static void
+selection_changed_cb (GtkTreeSelection *sel, GdauiTreeStore *store)
+{
+ GtkTreeIter iter;
+ if (gtk_tree_selection_get_selected (sel, NULL, &iter)) {
+ gchar *rdn;
+ gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, 0, &rdn, -1);
+ g_print ("Selected RDN: %s\n", rdn);
+ g_free (rdn);
+
+ GdaTreeNode *node;
+ const GValue *cvalue;
+ node = gdaui_tree_store_get_node (store, &iter);
+ g_assert (node);
+ cvalue = gda_tree_node_get_node_attribute (node, "dn");
+ g_assert (cvalue);
+ g_print (" DN: %s\n", g_value_get_string (cvalue));
+ }
+}
+
+static GtkWidget *
+create_window (GdaConnection *cnc)
+{
+ GtkWidget *win = NULL;
+
+ /* Window */
+ win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title (GTK_WINDOW (win), "LDAP Browser");
+ gtk_window_set_default_size (GTK_WINDOW (win), 640, 480);
+ gtk_container_set_border_width (GTK_CONTAINER (win), 5);
+ gtk_window_set_position (GTK_WINDOW (win), GTK_WIN_POS_CENTER);
+ g_signal_connect (G_OBJECT (win), "destroy", gtk_main_quit, NULL);
+
+ /* container */
+ GtkWidget *vb, *hp;
+ vb = gtk_vbox_new (FALSE, 0);
+ gtk_container_add (GTK_CONTAINER (win), vb);
+ hp = gtk_hpaned_new ();
+ gtk_box_pack_start (GTK_BOX (vb), hp, TRUE, TRUE, 0);
+
+ GdaTree *tree;
+ GdaTreeManager *mgr;
+ GtkTreeModel *store;
+ GtkWidget *tview, *sw;
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+
+ tree = gda_tree_new ();
+ mgr = gda_tree_mgr_ldap_new (cnc, NULL);
+ gda_tree_add_manager (tree, mgr);
+ gda_tree_manager_add_manager (mgr, mgr);
+ g_object_unref (mgr);
+
+ gda_tree_update_children (tree, NULL, NULL);
+
+ store = gdaui_tree_store_new (tree, 1, G_TYPE_STRING, "rdn");
+ g_object_unref (tree);
+ tview = gtk_tree_view_new_with_model (store);
+ g_signal_connect (tview, "test-expand-row",
+ G_CALLBACK (test_expand_row_cb), tree);
+
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("DN", renderer, "text", 0, NULL);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (tview), column);
+ gtk_tree_view_set_expander_column (GTK_TREE_VIEW (tview), column);
+
+ sw = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+ gtk_container_add (GTK_CONTAINER (sw), tview);
+ gtk_paned_add1 (GTK_PANED (hp), sw);
+
+ /* selection */
+ GtkTreeSelection *sel;
+ sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (tview));
+ gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
+ g_signal_connect (sel, "changed",
+ G_CALLBACK (selection_changed_cb), store);
+
+ gtk_widget_show_all (vb);
+
+ return win;
+}
+
+/*
+ * Entree du programme:
+ */
+int main (int argc, char ** argv)
+{
+ GdaConnection *cnc;
+ GOptionContext *context;
+ GError *error = NULL;
+
+ context = g_option_context_new (NULL);
+ g_option_context_add_main_entries (context, entries, NULL);
+ if (!g_option_context_parse (context, &argc, &argv, &error)) {
+ g_print ("Can't parse arguments: %s", error->message);
+ exit (1);
+ }
+ g_option_context_free (context);
+
+ gtk_init (& argc, & argv);
+ gdaui_init ();
+
+ cnc = open_ldap_connection ();
+ gtk_widget_show (create_window (cnc));
+ gtk_main ();
+
+ g_object_unref (cnc);
+
+ return 0;
+}
diff --git a/samples/Makefile b/samples/Makefile
index 06cddce..2431a36 100644
--- a/samples/Makefile
+++ b/samples/Makefile
@@ -1,5 +1,5 @@
SHELL= /bin/sh
-SUBDIRS = BDB DDL DirDataModel F-Spot Report SimpleExample SqlParserConsole TableCopy Virtual XSLT MetaStore Tree SqlBuilder AsyncExec Gir
+SUBDIRS = BDB DDL DirDataModel F-Spot Report SimpleExample SqlParserConsole TableCopy Virtual XSLT MetaStore Tree SqlBuilder AsyncExec Gir LdapBrowser
all:
for dir in ${SUBDIRS} ; do ( cd $$dir ; ${MAKE} ) ; done
clean:
diff --git a/testing/Makefile.am b/testing/Makefile.am
index 2416150..c5b808f 100644
--- a/testing/Makefile.am
+++ b/testing/Makefile.am
@@ -2,6 +2,7 @@ AM_CPPFLAGS = \
-I$(top_srcdir) \
-I$(top_srcdir)/libgda \
-I$(top_srcdir)/libgda/sqlite \
+ -I$(top_builddir)/libgda/sqlite \
-I$(top_builddir) \
$(COREDEPS_CFLAGS) \
$(COREDEPS_WFLAGS)
diff --git a/tests/data-models/check_pmodel.c b/tests/data-models/check_pmodel.c
index a3260c5..925a462 100644
--- a/tests/data-models/check_pmodel.c
+++ b/tests/data-models/check_pmodel.c
@@ -811,7 +811,7 @@ test7 (GdaConnection *cnc)
{
GError *error = NULL;
GdaDataModel *model = NULL;
- GdaStatement *stmt;
+ GdaStatement *stmt = NULL;
gint nfailed = 0;
GdaSet *params;
GValue *value;
diff --git a/tools/.gitignore b/tools/.gitignore
index 09a88dd..4b8d848 100644
--- a/tools/.gitignore
+++ b/tools/.gitignore
@@ -4,3 +4,4 @@ gda-list-server-op-5*
information-schema-doc
information-schema-types
gda-sql-5.*.1
+gda-ldap-browser-5*
diff --git a/tools/Makefile.am b/tools/Makefile.am
index 59cf513..da086be 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -15,6 +15,7 @@ AM_CPPFLAGS = \
-I$(top_srcdir) \
-I$(top_builddir) \
-I$(top_srcdir)/libgda/sqlite \
+ -I$(top_builddir)/libgda/sqlite \
-I$(top_srcdir)/libgda \
-I. \
$(COREDEPS_CFLAGS) \
diff --git a/tools/browser/Makefile.am b/tools/browser/Makefile.am
index 1a0becb..80a54ac 100644
--- a/tools/browser/Makefile.am
+++ b/tools/browser/Makefile.am
@@ -2,6 +2,13 @@ bin_PROGRAMS=gda-browser-5.0
noinst_LTLIBRARIES = libbrowser.la
SUBDIRS = data common schema-browser query-exec data-manager dummy-perspective
+
+if LDAP
+ldap_flags=-DHAVE_LDAP
+SUBDIRS+=ldap-browser
+LDAP_LDADD=$(top_builddir)/tools/browser/ldap-browser/libperspective.la
+endif
+
if HAVE_GOOCANVAS
SUBDIRS+=canvas
noinst_PROGRAMS=canvas-example
@@ -15,6 +22,7 @@ AM_CPPFLAGS = \
-I$(top_srcdir) \
-I$(top_srcdir)/libgda \
-I$(top_srcdir)/libgda/sqlite \
+ -I$(top_builddir)/libgda/sqlite \
-I$(top_builddir) \
$(COREDEPS_CFLAGS) \
$(COREDEPS_WFLAGS) \
@@ -24,7 +32,8 @@ AM_CPPFLAGS = \
-DPREFIX=\""$(prefix)"\" \
-DSYSCONFDIR=\""$(sysconfdir)"\" \
-DDATADIR=\""$(datadir)"\" \
- -DLIBDIR=\""$(libdir)"\"
+ -DLIBDIR=\""$(libdir)"\" \
+ $(ldap_flags)
marshal.h: marshal.list $(GLIB_GENMARSHAL)
$(GLIB_GENMARSHAL) $< --header --prefix=_marshal > $@
@@ -104,6 +113,7 @@ gda_browser_5_0_LDADD=\
schema-browser/libperspective.la \
query-exec/libperspective.la \
data-manager/libperspective.la \
+ $(LDAP_LDADD) \
libbrowser.la \
$(top_builddir)/libgda-ui/internal/libgda-ui-internal.la \
$(CANVAS_LDADD) \
@@ -167,7 +177,15 @@ icons_DATA= \
gda-browser-action.png \
gda-browser-form.png \
gda-browser-grid.png \
- gda-browser-menu-ind.png
+ gda-browser-menu-ind.png \
+ gda-browser-ldap-entry.png \
+ gda-browser-ldap-group.png \
+ gda-browser-ldap-organization.png \
+ gda-browser-ldap-person.png \
+ gda-browser-ldap-class-a.png \
+ gda-browser-ldap-class-s.png \
+ gda-browser-ldap-class-x.png \
+ gda-browser-ldap-class-u.png
# app icon
appiconsdir=$(datadir)/pixmaps
diff --git a/tools/browser/auth-dialog.c b/tools/browser/auth-dialog.c
index 007ca9a..7cf05ec 100644
--- a/tools/browser/auth-dialog.c
+++ b/tools/browser/auth-dialog.c
@@ -305,6 +305,13 @@ sub_thread_open_cnc (AuthData *ad, GError **error)
GDA_CONNECTION_OPTIONS_THREAD_SAFE |
GDA_CONNECTION_OPTIONS_AUTO_META_DATA,
error);
+#ifdef HAVE_LDAP
+ if (cnc && GDA_IS_LDAP_CONNECTION (cnc)) {
+ /* force classes init */
+ GdaLdapClass *lcl;
+ lcl = gda_ldap_get_class_info (GDA_LDAP_CONNECTION (cnc), "top");
+ }
+#endif
return cnc;
#else
sleep (5);
diff --git a/tools/browser/browser-connection.c b/tools/browser/browser-connection.c
index 08829a7..d693138 100644
--- a/tools/browser/browser-connection.c
+++ b/tools/browser/browser-connection.c
@@ -70,13 +70,18 @@ static gboolean check_for_wrapper_result (BrowserConnection *bcnc);
typedef enum {
JOB_TYPE_META_STORE_UPDATE,
JOB_TYPE_META_STRUCT_SYNC,
- JOB_TYPE_STATEMENT_EXECUTE
+ JOB_TYPE_STATEMENT_EXECUTE,
+ JOB_TYPE_CALLBACK
} JobType;
typedef struct {
guint job_id;
JobType job_type;
gchar *reason;
+
+ /* the following may be %NULL for stmt execution and meta store updates */
+ BrowserConnectionJobCallback callback;
+ gpointer cb_data;
} WrapperJob;
static void
@@ -90,7 +95,8 @@ wrapper_job_free (WrapperJob *wj)
* Pushes a job which has been asked to be exected in a sub thread using gda_thread_wrapper_execute()
*/
static void
-push_wrapper_job (BrowserConnection *bcnc, guint job_id, JobType job_type, const gchar *reason)
+push_wrapper_job (BrowserConnection *bcnc, guint job_id, JobType job_type, const gchar *reason,
+ BrowserConnectionJobCallback callback, gpointer cb_data)
{
/* setup timer */
if (bcnc->priv->wrapper_results_timer == 0) {
@@ -116,6 +122,8 @@ push_wrapper_job (BrowserConnection *bcnc, guint job_id, JobType job_type, const
wj->job_type = job_type;
if (reason)
wj->reason = g_strdup (reason);
+ wj->callback = callback;
+ wj->cb_data = cb_data;
bcnc->priv->wrapper_jobs = g_slist_append (bcnc->priv->wrapper_jobs, wj);
@@ -355,7 +363,7 @@ meta_changed_cb (G_GNUC_UNUSED GdaThreadWrapper *wrapper,
g_object_ref (bcnc), g_object_unref, &lerror);
if (job_id > 0)
push_wrapper_job (bcnc, job_id, JOB_TYPE_META_STRUCT_SYNC,
- _("Analysing database schema"));
+ _("Analysing database schema"), NULL, NULL);
else if (lerror) {
browser_show_error (NULL, _("Error while fetching meta data from the connection: %s"),
lerror->message ? lerror->message : _("No detail"));
@@ -442,7 +450,8 @@ browser_connection_set_property (GObject *object,
g_object_ref (bcnc), g_object_unref, &lerror);
if (job_id > 0)
push_wrapper_job (bcnc, job_id, JOB_TYPE_META_STORE_UPDATE,
- _("Getting database schema information"));
+ _("Getting database schema information"),
+ NULL, NULL);
else if (lerror) {
browser_show_error (NULL, _("Error while fetching meta data from the connection: %s"),
lerror->message ? lerror->message : _("No detail"));
@@ -462,7 +471,8 @@ browser_connection_set_property (GObject *object,
g_object_ref (bcnc), g_object_unref, &lerror);
if (job_id > 0)
push_wrapper_job (bcnc, job_id, JOB_TYPE_META_STRUCT_SYNC,
- _("Analysing database schema"));
+ _("Analysing database schema"),
+ NULL, NULL);
else if (lerror) {
browser_show_error (NULL, _("Error while fetching meta data from the connection: %s"),
lerror->message ? lerror->message : _("No detail"));
@@ -475,6 +485,7 @@ browser_connection_set_property (GObject *object,
FALSE, FALSE,
(GdaThreadWrapperCallback) meta_changed_cb,
bcnc);
+
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
@@ -729,6 +740,16 @@ check_for_wrapper_result (BrowserConnection *bcnc)
g_hash_table_insert (bcnc->priv->executed_statements, id, res);
break;
}
+ case JOB_TYPE_CALLBACK:
+ if (wj->callback) {
+ wj->callback (bcnc, exec_res == (gpointer) 0x01 ? NULL : exec_res,
+ wj->cb_data, lerror);
+ g_clear_error (&lerror);
+ }
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
}
pop_wrapper_job (bcnc, wj);
@@ -900,7 +921,7 @@ browser_connection_update_meta_data (BrowserConnection *bcnc)
g_object_ref (bcnc), g_object_unref, &lerror);
if (job_id > 0)
push_wrapper_job (bcnc, job_id, JOB_TYPE_META_STORE_UPDATE,
- _("Getting database schema information"));
+ _("Getting database schema information"), NULL, NULL);
else if (lerror) {
browser_show_error (NULL, _("Error while fetching meta data from the connection: %s"),
lerror->message ? lerror->message : _("No detail"));
@@ -1011,13 +1032,13 @@ browser_connection_rollback (BrowserConnection *bcnc, GError **error)
*
* Get @bcnc's favorites handler
*
- * Returns: the #BrowserFavorites used by @bcnc
+ * Returns: (transfer none): the #BrowserFavorites used by @bcnc
*/
BrowserFavorites *
browser_connection_get_favorites (BrowserConnection *bcnc)
{
g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), NULL);
- if (!bcnc->priv->bfav) {
+ if (!bcnc->priv->bfav && !BROWSER_IS_VIRTUAL_CONNECTION (bcnc)) {
bcnc->priv->bfav = browser_favorites_new (gda_connection_get_meta_store (bcnc->priv->cnc));
g_signal_connect (bcnc->priv->bfav, "favorites-changed",
G_CALLBACK (fav_changed_cb), bcnc);
@@ -1100,10 +1121,11 @@ wrapper_statement_execute (StmtExecData *data, GError **error)
{
GObject *obj;
GdaSet *last_insert_row = NULL;
+ GError *lerror = NULL;
obj = gda_connection_statement_execute (data->cnc, data->stmt,
data->params, data->model_usage,
data->need_last_insert_row ? &last_insert_row : NULL,
- error);
+ &lerror);
if (obj) {
if (GDA_IS_DATA_MODEL (obj))
/* force loading of rows if necessary */
@@ -1111,6 +1133,15 @@ wrapper_statement_execute (StmtExecData *data, GError **error)
else if (last_insert_row)
g_object_set_data (obj, "__bcnc_last_inserted_row", last_insert_row);
}
+ else {
+ if (lerror)
+ g_propagate_error (error, lerror);
+ else {
+ g_warning (_("Execution reported an undefined error, please report error to "
+ "http://bugzilla.gnome.org/ for the \"libgda\" product"));
+ g_set_error (error, 0, 0, _("No detail"));
+ }
+ }
return obj ? obj : (gpointer) 0x01;
}
@@ -1156,7 +1187,7 @@ browser_connection_execute_statement (BrowserConnection *bcnc,
data, (GDestroyNotify) g_free, error);
if (job_id > 0)
push_wrapper_job (bcnc, job_id, JOB_TYPE_STATEMENT_EXECUTE,
- _("Executing a query"));
+ _("Executing a query"), NULL, NULL);
return job_id;
}
@@ -1206,7 +1237,7 @@ browser_connection_rerun_select (BrowserConnection *bcnc,
data, (GDestroyNotify) g_free, error);
if (job_id > 0)
push_wrapper_job (bcnc, job_id, JOB_TYPE_STATEMENT_EXECUTE,
- _("Executing a query"));
+ _("Executing a query"), NULL, NULL);
return job_id;
}
@@ -1261,6 +1292,23 @@ browser_connection_execution_get_result (BrowserConnection *bcnc, guint exec_id,
return retval;
}
+/**
+ * browser_connection_job_cancel:
+ * @bcnc: a #BrowserConnection
+ * @job_id: the job_id to cancel
+ *
+ * Cancel a job, from the job ID returned by browser_connection_ldap_describe_entry()
+ * or browser_connection_ldap_get_entry_children().
+ */
+void
+browser_connection_job_cancel (BrowserConnection *bcnc, guint job_id)
+{
+ g_return_if_fail (BROWSER_IS_CONNECTION (bcnc));
+ g_return_if_fail (job_id > 0);
+
+ TO_IMPLEMENT;
+}
+
static gboolean query_exec_fetch_cb (BrowserConnection *bcnc);
typedef struct {
@@ -2143,3 +2191,442 @@ browser_connection_load_variables (BrowserConnection *bcnc, GdaSet *set)
}
}
}
+
+/**
+ * browser_connection_is_ldap:
+ * @bcnc: a #BrowserConnection
+ *
+ * Returns: %TRUE if @bcnc proxies an LDAP connection
+ */
+gboolean
+browser_connection_is_ldap (BrowserConnection *bcnc)
+{
+ g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), FALSE);
+
+#ifdef HAVE_LDAP
+ return GDA_IS_LDAP_CONNECTION (bcnc->priv->cnc) ? TRUE : FALSE;
+#endif
+ return FALSE;
+}
+
+#ifdef HAVE_LDAP
+
+typedef struct {
+ GdaConnection *cnc;
+ gchar *base_dn;
+ gchar *attributes;
+ gchar *filter;
+ GdaLdapSearchScope scope;
+} LdapSearchData;
+static void
+ldap_search_data_free (LdapSearchData *data)
+{
+ g_free (data->base_dn);
+ g_free (data->filter);
+ g_free (data->attributes);
+ g_free (data);
+}
+
+/* executed in sub @bcnc->priv->wrapper's thread */
+static gpointer
+wrapper_ldap_search (LdapSearchData *data, GError **error)
+{
+ GdaDataModel *model;
+ model = gda_data_model_ldap_new (GDA_CONNECTION (data->cnc), data->base_dn,
+ data->filter, data->attributes, data->scope);
+ if (!model) {
+ g_set_error (error, 0, 0,
+ _("Could not execute LDAP search"));
+ return (gpointer) 0x01;
+ }
+ else {
+ GdaDataModel *wrapped;
+ gint nb;
+ wrapped = gda_data_access_wrapper_new (model);
+ g_object_unref (model);
+ /* force loading all the LDAP entries in memory to avoid
+ * having the GTK thread lock on LDAP searches */
+ nb = gda_data_model_get_n_rows (wrapped);
+ return wrapped;
+ }
+}
+
+/**
+ * browser_connection_ldap_search:
+ *
+ * Executes an LDAP search. Wrapper around gda_data_model_ldap_new()
+ *
+ * Returns: a job ID, or %0 if an error occurred
+ */
+guint
+browser_connection_ldap_search (BrowserConnection *bcnc,
+ const gchar *base_dn, const gchar *filter,
+ const gchar *attributes, GdaLdapSearchScope scope,
+ BrowserConnectionJobCallback callback,
+ gpointer cb_data, GError **error)
+{
+ g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), 0);
+ g_return_val_if_fail (GDA_IS_LDAP_CONNECTION (bcnc->priv->cnc), 0);
+
+ LdapSearchData *data;
+ guint job_id;
+ data = g_new0 (LdapSearchData, 1);
+ data->cnc = bcnc->priv->cnc;
+ data->base_dn = g_strdup (base_dn);
+ data->filter = g_strdup (filter);
+ data->attributes = g_strdup (attributes);
+ data->scope = scope;
+
+ job_id = gda_thread_wrapper_execute (bcnc->priv->wrapper,
+ (GdaThreadWrapperFunc) wrapper_ldap_search,
+ data, (GDestroyNotify) ldap_search_data_free, error);
+ if (job_id > 0)
+ push_wrapper_job (bcnc, job_id, JOB_TYPE_CALLBACK,
+ _("Executing LDAP search"), callback, cb_data);
+ return job_id;
+}
+
+
+typedef struct {
+ GdaConnection *cnc;
+ gchar *dn;
+ gchar **attributes;
+} LdapData;
+static void
+ldap_data_free (LdapData *data)
+{
+ g_free (data->dn);
+ if (data->attributes)
+ g_strfreev (data->attributes);
+ g_free (data);
+}
+
+/* executed in sub @bcnc->priv->wrapper's thread */
+static gpointer
+wrapper_ldap_describe_entry (LdapData *data, GError **error)
+{
+ GdaLdapEntry *lentry;
+ lentry = gda_ldap_describe_entry (GDA_LDAP_CONNECTION (data->cnc), data->dn, error);
+ return lentry ? lentry : (gpointer) 0x01;
+}
+
+/**
+ * browser_connection_ldap_describe_entry:
+ * @bcnc: a #BrowserConnection
+ * @dn: the DN of the entry to describe
+ * @callback: the callback to execute with the results
+ * @cb_data: a pointer to pass to @callback
+ * @error: a place to store errors, or %NULL
+ *
+ * Wrapper around gda_ldap_describe_entry().
+ *
+ * Returns: a job ID, or %0 if an error occurred
+ */
+guint
+browser_connection_ldap_describe_entry (BrowserConnection *bcnc, const gchar *dn,
+ BrowserConnectionJobCallback callback,
+ gpointer cb_data, GError **error)
+{
+ g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), 0);
+ g_return_val_if_fail (GDA_IS_LDAP_CONNECTION (bcnc->priv->cnc), 0);
+
+ LdapData *data;
+ guint job_id;
+
+ data = g_new0 (LdapData, 1);
+ data->cnc = bcnc->priv->cnc;
+ data->dn = g_strdup (dn);
+
+ job_id = gda_thread_wrapper_execute (bcnc->priv->wrapper,
+ (GdaThreadWrapperFunc) wrapper_ldap_describe_entry,
+ data, (GDestroyNotify) ldap_data_free, error);
+ if (job_id > 0)
+ push_wrapper_job (bcnc, job_id, JOB_TYPE_CALLBACK,
+ _("Fetching LDAP entry's attributes"), callback, cb_data);
+
+ return job_id;
+}
+
+/* executed in sub @bcnc->priv->wrapper's thread */
+static gpointer
+wrapper_ldap_get_entry_children (LdapData *data, GError **error)
+{
+ GdaLdapEntry **array;
+ array = gda_ldap_get_entry_children (GDA_LDAP_CONNECTION (data->cnc), data->dn, data->attributes, error);
+ return array ? array : (gpointer) 0x01;
+}
+
+/**
+ * browser_connection_ldap_get_entry_children:
+ * @bcnc: a #BrowserConnection
+ * @dn: the DN of the entry to get children from
+ * @callback: the callback to execute with the results
+ * @cb_data: a pointer to pass to @callback
+ * @error: a place to store errors, or %NULL
+ *
+ * Wrapper around gda_ldap_get_entry_children().
+ *
+ * Returns: a job ID, or %0 if an error occurred
+ */
+guint
+browser_connection_ldap_get_entry_children (BrowserConnection *bcnc, const gchar *dn,
+ gchar **attributes,
+ BrowserConnectionJobCallback callback,
+ gpointer cb_data, GError **error)
+{
+ g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), 0);
+ g_return_val_if_fail (GDA_IS_LDAP_CONNECTION (bcnc->priv->cnc), 0);
+
+ LdapData *data;
+ guint job_id;
+
+ data = g_new0 (LdapData, 1);
+ data->cnc = bcnc->priv->cnc;
+ data->dn = g_strdup (dn);
+ if (attributes) {
+ gint i;
+ GArray *array = NULL;
+ for (i = 0; attributes [i]; i++) {
+ gchar *tmp;
+ if (! array)
+ array = g_array_new (TRUE, FALSE, sizeof (gchar*));
+ tmp = g_strdup (attributes [i]);
+ g_array_append_val (array, tmp);
+ }
+ if (array)
+ data->attributes = (gchar**) g_array_free (array, FALSE);
+ }
+
+ job_id = gda_thread_wrapper_execute (bcnc->priv->wrapper,
+ (GdaThreadWrapperFunc) wrapper_ldap_get_entry_children,
+ data, (GDestroyNotify) ldap_data_free, error);
+ if (job_id > 0)
+ push_wrapper_job (bcnc, job_id, JOB_TYPE_CALLBACK,
+ _("Fetching LDAP entry's children"), callback, cb_data);
+
+ return job_id;
+}
+
+/**
+ * browser_connection_ldap_icon_for_class:
+ * @objectclass: objectClass attribute
+ *
+ * Returns: the correct icon, or %NULL if it could not be determined
+ */
+GdkPixbuf *
+browser_connection_ldap_icon_for_class (GdaLdapAttribute *objectclass)
+{
+ gint type = 0;
+ if (objectclass) {
+ guint i;
+ for (i = 0; i < objectclass->nb_values; i++) {
+ const gchar *class = g_value_get_string (objectclass->values[i]);
+ if (!class)
+ continue;
+ else if (!strcmp (class, "organization"))
+ type = MAX(type, 1);
+ else if (!strcmp (class, "groupOfNames") ||
+ !strcmp (class, "posixGroup"))
+ type = MAX(type, 2);
+ else if (!strcmp (class, "account") ||
+ !strcmp (class, "mailUser") ||
+ !strcmp (class, "organizationalPerson") ||
+ !strcmp (class, "person") ||
+ !strcmp (class, "pilotPerson") ||
+ !strcmp (class, "newPilotPerson") ||
+ !strcmp (class, "pkiUser") ||
+ !strcmp (class, "posixUser") ||
+ !strcmp (class, "posixAccount") ||
+ !strcmp (class, "residentalPerson") ||
+ !strcmp (class, "shadowAccount") ||
+ !strcmp (class, "strongAuthenticationUser") ||
+ !strcmp (class, "inetOrgPerson"))
+ type = MAX(type, 3);
+ }
+ }
+
+ switch (type) {
+ case 0:
+ return browser_get_pixbuf_icon (BROWSER_ICON_LDAP_ENTRY);
+ case 1:
+ return browser_get_pixbuf_icon (BROWSER_ICON_LDAP_ORGANIZATION);
+ case 2:
+ return browser_get_pixbuf_icon (BROWSER_ICON_LDAP_GROUP);
+ case 3:
+ return browser_get_pixbuf_icon (BROWSER_ICON_LDAP_PERSON);
+ default:
+ g_assert_not_reached ();
+ }
+ return NULL;
+}
+
+typedef struct {
+ BrowserConnectionJobCallback callback;
+ gpointer cb_data;
+} IconTypeData;
+
+static void
+fetch_classes_cb (G_GNUC_UNUSED BrowserConnection *bcnc,
+ gpointer out_result, IconTypeData *data, G_GNUC_UNUSED GError *error)
+{
+ GdkPixbuf *pixbuf = NULL;
+ if (out_result) {
+ GdaLdapEntry *lentry = (GdaLdapEntry*) out_result;
+ GdaLdapAttribute *attr;
+ attr = g_hash_table_lookup (lentry->attributes_hash, "objectClass");
+ pixbuf = browser_connection_ldap_icon_for_class (attr);
+ gda_ldap_entry_free (lentry);
+ }
+ if (data->callback)
+ data->callback (bcnc, pixbuf, data->cb_data, error);
+
+ g_free (data);
+}
+
+/**
+ * browser_connection_ldap_icon_for_dn:
+ * @bcnc: a #BrowserConnection
+ * @dn: the DN of the entry
+ * @callback: the callback to execute with the results
+ * @cb_data: a pointer to pass to @callback
+ * @error: a place to store errors, or %NULL
+ *
+ * Determines the correct icon type for @dn based on which class it belongs to
+ *
+ * Returns: a job ID, or %0 if an error occurred
+ */
+guint
+browser_connection_ldap_icon_for_dn (BrowserConnection *bcnc, const gchar *dn,
+ BrowserConnectionJobCallback callback,
+ gpointer cb_data, GError **error)
+{
+ IconTypeData *data;
+ guint job_id;
+ g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), 0);
+ g_return_val_if_fail (GDA_IS_LDAP_CONNECTION (bcnc->priv->cnc), 0);
+ g_return_val_if_fail (dn && *dn, 0);
+
+ data = g_new (IconTypeData, 1);
+ data->callback = callback;
+ data->cb_data = cb_data;
+ job_id = browser_connection_ldap_describe_entry (bcnc, dn,
+ BROWSER_CONNECTION_JOB_CALLBACK (fetch_classes_cb),
+ data, error);
+ return job_id;
+}
+
+/**
+ * browser_connection_ldap_get_base_dn:
+ * @bcnc: a #BrowserConnection
+ *
+ * wrapper for gda_ldap_connection_get_base_dn()
+ *
+ * Returns: the base DN or %NULL
+ */
+const gchar *
+browser_connection_ldap_get_base_dn (BrowserConnection *bcnc)
+{
+ g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), NULL);
+ g_return_val_if_fail (GDA_IS_LDAP_CONNECTION (bcnc->priv->cnc), NULL);
+
+ return gda_ldap_connection_get_base_dn (GDA_LDAP_CONNECTION (bcnc->priv->cnc));
+}
+
+/**
+ * browser_connection_describe_table:
+ * @bcnc: a #BrowserConnection
+ * @table_name: a table name, not %NULL
+ * @out_base_dn: (allow-none) (transfer none): a place to store the LDAP search base DN, or %NULL
+ * @out_filter: (allow-none) (transfer none): a place to store the LDAP search filter, or %NULL
+ * @out_attributes: (allow-none) (transfer none): a place to store the LDAP search attributes, or %NULL
+ * @out_scope: (allow-none) (transfer none): a place to store the LDAP search scope, or %NULL
+ * @error: a place to store errors, or %NULL
+ */
+gboolean
+browser_connection_describe_table (BrowserConnection *bcnc, const gchar *table_name,
+ const gchar **out_base_dn, const gchar **out_filter,
+ const gchar **out_attributes,
+ GdaLdapSearchScope *out_scope, GError **error)
+{
+ g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), FALSE);
+ g_return_val_if_fail (browser_connection_is_ldap (bcnc), FALSE);
+ g_return_val_if_fail (table_name && *table_name, FALSE);
+
+ return gda_ldap_connection_describe_table (GDA_LDAP_CONNECTION (bcnc->priv->cnc),
+ table_name, out_base_dn, out_filter,
+ out_attributes, out_scope, error);
+}
+
+/**
+ * browser_connection_get_class_info:
+ * @bcnc: a #BrowserConnection
+ * @classname: a class name
+ *
+ * proxy for gda_ldap_get_class_info.
+ */
+GdaLdapClass *
+browser_connection_get_class_info (BrowserConnection *bcnc, const gchar *classname)
+{
+ g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), NULL);
+ g_return_val_if_fail (browser_connection_is_ldap (bcnc), NULL);
+
+ return gda_ldap_get_class_info (GDA_LDAP_CONNECTION (bcnc->priv->cnc), classname);
+}
+
+/**
+ * browser_connection_get_top_classes:
+ * @bcnc: a #BrowserConnection
+ *
+ * proxy for gda_ldap_get_top_classes.
+ */
+const GSList *
+browser_connection_get_top_classes (BrowserConnection *bcnc)
+{
+ g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), NULL);
+ g_return_val_if_fail (browser_connection_is_ldap (bcnc), NULL);
+
+ return gda_ldap_get_top_classes (GDA_LDAP_CONNECTION (bcnc->priv->cnc));
+}
+
+/**
+ * browser_connection_declare_table:
+ * @bcnc: a #BrowserConnection
+ *
+ * Wrapper around gda_ldap_connection_declare_table()
+ */
+gboolean
+browser_connection_declare_table (BrowserConnection *bcnc,
+ const gchar *table_name,
+ const gchar *base_dn,
+ const gchar *filter,
+ const gchar *attributes,
+ GdaLdapSearchScope scope,
+ GError **error)
+{
+ g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), FALSE);
+ g_return_val_if_fail (browser_connection_is_ldap (bcnc), FALSE);
+
+ return gda_ldap_connection_declare_table (GDA_LDAP_CONNECTION (bcnc->priv->cnc),
+ table_name, base_dn, filter,
+ attributes, scope, error);
+}
+
+/**
+ * browser_connection_undeclare_table:
+ * @bcnc: a #BrowserConnection
+ *
+ * Wrapper around gda_ldap_connection_undeclare_table()
+ */
+gboolean
+browser_connection_undeclare_table (BrowserConnection *bcnc,
+ const gchar *table_name,
+ GError **error)
+{
+ g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), FALSE);
+ g_return_val_if_fail (browser_connection_is_ldap (bcnc), FALSE);
+
+ return gda_ldap_connection_undeclare_table (GDA_LDAP_CONNECTION (bcnc->priv->cnc),
+ table_name, error);
+}
+
+#endif /* HAVE_LDAP */
diff --git a/tools/browser/browser-connection.h b/tools/browser/browser-connection.h
index 606d369..d07e8db 100644
--- a/tools/browser/browser-connection.h
+++ b/tools/browser/browser-connection.h
@@ -23,6 +23,10 @@
#include <libgda/libgda.h>
#include "browser-favorites.h"
#include "decl.h"
+#include "support.h"
+#ifdef HAVE_LDAP
+#include <libgda/sqlite/virtual/gda-ldap-connection.h>
+#endif
G_BEGIN_DECLS
@@ -88,6 +92,24 @@ BrowserFavorites *browser_connection_get_favorites (BrowserConnection
gchar **browser_connection_get_completions (BrowserConnection *bcnc, const gchar *sql,
gint start, gint end);
+/**
+ * BrowserConnectionJobCallback:
+ * @bcnc: the #BrowserConnection
+ * @out_result: the execution result
+ * @data: a pointer passed when calling the execution function such as browser_connection_ldap_describe_entry()
+ * @error: the error returned, if any
+ *
+ * Callback function called when a job (not a statement execution job) is finished.
+ * the out_result is not used by the BrowserConnection anymore after this function has been
+ * called, so you need to keep it or free it.
+ *
+ * @error should not be modified.
+ */
+typedef void (*BrowserConnectionJobCallback) (BrowserConnection *bcnc,
+ gpointer out_result, gpointer data, GError *error);
+#define BROWSER_CONNECTION_JOB_CALLBACK(x) ((BrowserConnectionJobCallback)(x))
+void browser_connection_job_cancel (BrowserConnection *bcnc, guint job_id);
+
/*
* statements's manipulations
*/
@@ -174,6 +196,49 @@ void browser_connection_define_ui_plugins_for_stmt (BrowserConne
void browser_connection_keep_variables (BrowserConnection *bcnc, GdaSet *set);
void browser_connection_load_variables (BrowserConnection *bcnc, GdaSet *set);
+/*
+ * LDAP
+ */
+gboolean browser_connection_is_ldap (BrowserConnection *bcnc);
+#ifdef HAVE_LDAP
+const gchar *browser_connection_ldap_get_base_dn (BrowserConnection *bcnc);
+guint browser_connection_ldap_search (BrowserConnection *bcnc,
+ const gchar *base_dn, const gchar *filter,
+ const gchar *attributes, GdaLdapSearchScope scope,
+ BrowserConnectionJobCallback callback,
+ gpointer cb_data, GError **error);
+guint browser_connection_ldap_describe_entry (BrowserConnection *bcnc, const gchar *dn,
+ BrowserConnectionJobCallback callback,
+ gpointer cb_data, GError **error);
+guint browser_connection_ldap_get_entry_children (BrowserConnection *bcnc, const gchar *dn,
+ gchar **attributes,
+ BrowserConnectionJobCallback callback,
+ gpointer cb_data, GError **error);
+guint browser_connection_ldap_icon_for_dn (BrowserConnection *bcnc, const gchar *dn,
+ BrowserConnectionJobCallback callback,
+ gpointer cb_data, GError **error);
+GdkPixbuf *browser_connection_ldap_icon_for_class (GdaLdapAttribute *objectclass);
+gboolean browser_connection_describe_table (BrowserConnection *bcnc, const gchar *table_name,
+ const gchar **out_base_dn, const gchar **out_filter,
+ const gchar **out_attributes,
+ GdaLdapSearchScope *out_scope, GError **error);
+
+GdaLdapClass *browser_connection_get_class_info (BrowserConnection *bcnc, const gchar *classname);
+const GSList *browser_connection_get_top_classes (BrowserConnection *bcnc);
+
+gboolean browser_connection_declare_table (BrowserConnection *bcnc,
+ const gchar *table_name,
+ const gchar *base_dn,
+ const gchar *filter,
+ const gchar *attributes,
+ GdaLdapSearchScope scope,
+ GError **error);
+gboolean browser_connection_undeclare_table (BrowserConnection *bcnc,
+ const gchar *table_name,
+ GError **error);
+
+#endif
+
G_END_DECLS
#endif
diff --git a/tools/browser/browser-favorites.c b/tools/browser/browser-favorites.c
index 2e7a0c7..5d8aca7 100644
--- a/tools/browser/browser-favorites.c
+++ b/tools/browser/browser-favorites.c
@@ -201,8 +201,14 @@ meta_store_addons_init (BrowserFavorites *bfav, GError **error)
return TRUE;
}
-static const gchar *
-favorite_type_to_string (BrowserFavoritesType type)
+/**
+ * browser_favorites_type_to_string:
+ * @type: a #BrowserFavoritesType
+ *
+ * Returns: a string representing @type
+ */
+const gchar *
+browser_favorites_type_to_string (BrowserFavoritesType type)
{
switch (type) {
case BROWSER_FAVORITES_TABLES:
@@ -215,6 +221,10 @@ favorite_type_to_string (BrowserFavoritesType type)
return "DATAMAN";
case BROWSER_FAVORITES_ACTIONS:
return "ACTION";
+ case BROWSER_FAVORITES_LDAP_DN:
+ return "LDAP_DN";
+ case BROWSER_FAVORITES_LDAP_CLASS:
+ return "LDAP_CLASS";
default:
g_warning ("Unknown type of favorite");
}
@@ -237,6 +247,12 @@ favorite_string_to_type (const gchar *str)
return BROWSER_FAVORITES_QUERIES;
else if (*str == 'A')
return BROWSER_FAVORITES_ACTIONS;
+ else if (*str == 'L') {
+ if (strlen (str) == 7)
+ return BROWSER_FAVORITES_LDAP_DN;
+ else
+ return BROWSER_FAVORITES_LDAP_CLASS;
+ }
else
g_warning ("Unknown type '%s' of favorite", str);
return 0;
@@ -555,7 +571,7 @@ browser_favorites_add (BrowserFavorites *bfav, guint session_id,
params = gda_set_new_inline (8,
"session", G_TYPE_INT, session_id,
"id", G_TYPE_INT, fav->id,
- "type", G_TYPE_STRING, favorite_type_to_string (rtype),
+ "type", G_TYPE_STRING, browser_favorites_type_to_string (rtype),
"name", G_TYPE_STRING, fav->name ? fav->name : efav.name,
"contents", G_TYPE_STRING, fav->contents,
"rank", G_TYPE_INT, pos,
@@ -719,7 +735,7 @@ browser_favorites_add (BrowserFavorites *bfav, guint session_id,
g_object_unref (params);
gda_lockable_unlock (GDA_LOCKABLE (store_cnc));
g_signal_emit (bfav, browser_favorites_signals [FAV_CHANGED],
- g_quark_from_string (favorite_type_to_string (rtype)));
+ g_quark_from_string (browser_favorites_type_to_string (rtype)));
return TRUE;
err:
@@ -842,7 +858,7 @@ browser_favorites_list (BrowserFavorites *bfav, guint session_id, BrowserFavorit
for (i = 0, flag = 1; i < BROWSER_FAVORITES_NB_TYPES; i++, flag <<= 1) {
if (type & flag) {
gchar *str;
- str = g_strdup_printf ("'%s'", favorite_type_to_string (flag));
+ str = g_strdup_printf ("'%s'", browser_favorites_type_to_string (flag));
or_cond_ids [or_cond_size] = gda_sql_builder_add_cond (b, GDA_SQL_OPERATOR_TYPE_EQ,
gda_sql_builder_add_id (b, "fav.type"),
gda_sql_builder_add_id (b, str),
@@ -1037,7 +1053,7 @@ browser_favorites_delete (BrowserFavorites *bfav, guint session_id,
gda_lockable_unlock (GDA_LOCKABLE (bfav->priv->store_cnc));
if (retval)
g_signal_emit (bfav, browser_favorites_signals [FAV_CHANGED],
- g_quark_from_string (favorite_type_to_string (efav.type)));
+ g_quark_from_string (browser_favorites_type_to_string (efav.type)));
browser_favorites_reset_attributes (&efav);
if (params)
g_object_unref (G_OBJECT (params));
diff --git a/tools/browser/browser-favorites.h b/tools/browser/browser-favorites.h
index 9444bf4..5358f2f 100644
--- a/tools/browser/browser-favorites.h
+++ b/tools/browser/browser-favorites.h
@@ -50,9 +50,16 @@ typedef enum {
BROWSER_FAVORITES_DIAGRAMS = 1 << 1,
BROWSER_FAVORITES_QUERIES = 1 << 2,
BROWSER_FAVORITES_DATA_MANAGERS = 1 << 3,
- BROWSER_FAVORITES_ACTIONS = 1 << 4
+ BROWSER_FAVORITES_ACTIONS = 1 << 4,
+ BROWSER_FAVORITES_LDAP_DN = 1 << 5,
+ BROWSER_FAVORITES_LDAP_CLASS = 1 << 6
} BrowserFavoritesType;
-#define BROWSER_FAVORITES_NB_TYPES 5
+#define BROWSER_FAVORITES_NB_TYPES 7
+
+#define ORDER_KEY_SCHEMA 1
+#define ORDER_KEY_QUERIES 2
+#define ORDER_KEY_DATA_MANAGERS 3
+#define ORDER_KEY_LDAP 4
/**
* BrowserFavoritesAttributes:
@@ -99,13 +106,14 @@ struct _BrowserFavoritesClass
GType browser_favorites_get_type (void) G_GNUC_CONST;
BrowserFavorites *browser_favorites_new (GdaMetaStore *store);
-
+const gchar *browser_favorites_type_to_string (BrowserFavoritesType type);
gboolean browser_favorites_add (BrowserFavorites *bfav, guint session_id,
BrowserFavoritesAttributes *fav,
gint order_key, gint pos,
GError **error);
GSList *browser_favorites_list (BrowserFavorites *bfav, guint session_id,
BrowserFavoritesType type, gint order_key, GError **error);
+
gboolean browser_favorites_delete (BrowserFavorites *bfav, guint session_id,
BrowserFavoritesAttributes *fav, GError **error);
void browser_favorites_free_list (GSList *fav_list);
diff --git a/tools/browser/browser-perspective.c b/tools/browser/browser-perspective.c
index 8a1e012..40c4b9b 100644
--- a/tools/browser/browser-perspective.c
+++ b/tools/browser/browser-perspective.c
@@ -22,6 +22,8 @@
#include "browser-perspective.h"
#include "browser-page.h"
+#include "browser-window.h"
+#include "support.h"
static GStaticRecMutex init_mutex = G_STATIC_REC_MUTEX_INIT;
static void browser_perspective_class_init (gpointer g_class);
@@ -150,3 +152,139 @@ browser_perspective_page_tab_label_change (BrowserPerspective *pers, BrowserPage
if (BROWSER_PERSPECTIVE_GET_CLASS (pers)->i_page_tab_label_change)
(BROWSER_PERSPECTIVE_GET_CLASS (pers)->i_page_tab_label_change) (pers, page);
}
+
+/**
+ * browser_perspective_get_window:
+ * @pers: an object implementing the #BrowserPerspective interface
+ *
+ * Returns: (transfer none): the #BrowserWindow @perspective is in
+ */
+BrowserWindow *
+browser_perspective_get_window (BrowserPerspective *pers)
+{
+ g_return_val_if_fail (IS_BROWSER_PERSPECTIVE (pers), NULL);
+ if (BROWSER_PERSPECTIVE_GET_CLASS (pers)->i_get_window)
+ return (BROWSER_PERSPECTIVE_GET_CLASS (pers)->i_get_window) (pers);
+ else
+ return (BrowserWindow*) browser_find_parent_widget (GTK_WIDGET (pers),
+ BROWSER_PERSPECTIVE_TYPE);
+}
+
+static void nb_page_added_or_removed_cb (GtkNotebook *nb, GtkWidget *child, guint page_num,
+ BrowserPerspective *perspective);
+static void nb_switch_page_cb (GtkNotebook *nb, GtkWidget *page, gint page_num,
+ BrowserPerspective *perspective);
+static void adapt_notebook_for_fullscreen (BrowserPerspective *perspective);;
+static void fullscreen_changed_cb (BrowserWindow *bwin, gboolean fullscreen, BrowserPerspective *perspective);
+
+/**
+ * browser_perspective_declare_notebook:
+ * @pers: an object implementing the #BrowserPerspective interface
+ * @nb: a #GtkNotebook
+ *
+ * Internally used by browser's perspectives to declare they internally use a notebook
+ * which state is modified when switching to and from fullscreen
+ */
+void
+browser_perspective_declare_notebook (BrowserPerspective *perspective, GtkNotebook *nb)
+{
+ BrowserWindow *bwin;
+ g_return_if_fail (IS_BROWSER_PERSPECTIVE (perspective));
+ g_return_if_fail (! nb || GTK_IS_NOTEBOOK (nb));
+
+ bwin = browser_perspective_get_window (perspective);
+ if (!bwin)
+ return;
+
+ GtkNotebook *onb;
+ onb = g_object_get_data (G_OBJECT (perspective), "fullscreen_nb");
+ if (onb) {
+ g_signal_handlers_disconnect_by_func (onb,
+ G_CALLBACK (nb_page_added_or_removed_cb),
+ perspective);
+ g_signal_handlers_disconnect_by_func (onb,
+ G_CALLBACK (nb_switch_page_cb),
+ perspective);
+ g_signal_handlers_disconnect_by_func (bwin,
+ G_CALLBACK (fullscreen_changed_cb), perspective);
+
+ }
+
+ g_object_set_data (G_OBJECT (perspective), "fullscreen_nb", nb);
+ if (nb) {
+ g_signal_connect (bwin, "fullscreen-changed",
+ G_CALLBACK (fullscreen_changed_cb), perspective);
+ g_signal_connect (nb, "page-added",
+ G_CALLBACK (nb_page_added_or_removed_cb), perspective);
+ g_signal_connect (nb, "page-removed",
+ G_CALLBACK (nb_page_added_or_removed_cb), perspective);
+ g_signal_connect (nb, "switch-page",
+ G_CALLBACK (nb_switch_page_cb), perspective);
+ adapt_notebook_for_fullscreen (perspective);
+ }
+}
+
+static void
+nb_switch_page_cb (GtkNotebook *nb, GtkWidget *page, gint page_num, BrowserPerspective *perspective)
+{
+ GtkWidget *page_contents;
+ GtkActionGroup *actions = NULL;
+ const gchar *ui = NULL;
+ BrowserWindow *bwin;
+
+ page_contents = gtk_notebook_get_nth_page (nb, page_num);
+ if (IS_BROWSER_PAGE (page_contents)) {
+ actions = browser_page_get_actions_group (BROWSER_PAGE (page_contents));
+ ui = browser_page_get_actions_ui (BROWSER_PAGE (page_contents));
+ }
+
+ bwin = browser_perspective_get_window (perspective);
+ if (bwin)
+ browser_window_customize_perspective_ui (bwin, perspective, actions, ui);
+ if (actions)
+ g_object_unref (actions);
+}
+
+static void
+nb_page_added_or_removed_cb (GtkNotebook *nb, GtkWidget *child, guint page_num,
+ BrowserPerspective *perspective)
+{
+ adapt_notebook_for_fullscreen (perspective);
+ if (gtk_notebook_get_n_pages (nb) == 0) {
+ BrowserWindow *bwin;
+ bwin = browser_perspective_get_window (perspective);
+ if (!bwin)
+ return;
+ browser_window_customize_perspective_ui (bwin,
+ BROWSER_PERSPECTIVE (perspective),
+ NULL, NULL);
+ }
+}
+
+static void
+fullscreen_changed_cb (G_GNUC_UNUSED BrowserWindow *bwin, gboolean fullscreen,
+ BrowserPerspective *perspective)
+{
+ adapt_notebook_for_fullscreen (perspective);
+}
+
+static void
+adapt_notebook_for_fullscreen (BrowserPerspective *perspective)
+{
+ gboolean showtabs = TRUE;
+ gboolean fullscreen;
+ BrowserWindow *bwin;
+ GtkNotebook *nb;
+
+ bwin = browser_perspective_get_window (perspective);
+ if (!bwin)
+ return;
+ nb = g_object_get_data (G_OBJECT (perspective), "fullscreen_nb");
+ if (!nb)
+ return;
+
+ fullscreen = browser_window_is_fullscreen (bwin);
+ if (fullscreen && gtk_notebook_get_n_pages (nb) == 1)
+ showtabs = FALSE;
+ gtk_notebook_set_show_tabs (nb, showtabs);
+}
diff --git a/tools/browser/browser-perspective.h b/tools/browser/browser-perspective.h
index ab7daf4..1e54bea 100644
--- a/tools/browser/browser-perspective.h
+++ b/tools/browser/browser-perspective.h
@@ -38,6 +38,7 @@ struct _BrowserPerspectiveIface {
GTypeInterface g_iface;
/* virtual table */
+ BrowserWindow *(* i_get_window) (BrowserPerspective *perspective);
GtkActionGroup *(* i_get_actions_group) (BrowserPerspective *perspective);
const gchar *(* i_get_actions_ui) (BrowserPerspective *perspective);
void (* i_get_current_customization) (BrowserPerspective *perspective,
@@ -67,6 +68,9 @@ void browser_perspective_get_current_customization (BrowserPerspectiv
void browser_perspective_page_tab_label_change (BrowserPerspective *perspective,
BrowserPage *page);
+BrowserWindow *browser_perspective_get_window (BrowserPerspective *perspective);
+void browser_perspective_declare_notebook (BrowserPerspective *perspective, GtkNotebook *nb);
+
G_END_DECLS
#endif
diff --git a/tools/browser/browser-stock-icons.c b/tools/browser/browser-stock-icons.c
index 0fa9d4d..965d779 100644
--- a/tools/browser/browser-stock-icons.c
+++ b/tools/browser/browser-stock-icons.c
@@ -1,6 +1,6 @@
/*
* Copyright © 2002 Jorn Baayen
- * Copyright © 2009 Vivien Malerba <malerba nome-db org>
+ * Copyright © 2009 - 2011 Vivien Malerba <malerba nome-db org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -48,6 +48,8 @@ browser_stock_icons_init (void)
{ BROWSER_STOCK_COMMIT, N_("Commit"), 0, 0, NULL },
{ BROWSER_STOCK_ROLLBACK, N_("Rollback"), 0, 0, NULL },
{ BROWSER_STOCK_BUILDER, N_("Builder"), 0, 0, NULL },
+ { BROWSER_STOCK_LDAP_ENTRIES, N_("Ldap entries"), 0, 0, NULL },
+ { BROWSER_STOCK_TABLE_ADD, N_("Add table"), 0, 0, NULL},
};
factory = gtk_icon_factory_new ();
diff --git a/tools/browser/browser-stock-icons.h b/tools/browser/browser-stock-icons.h
index 4f132d4..6b1b230 100644
--- a/tools/browser/browser-stock-icons.h
+++ b/tools/browser/browser-stock-icons.h
@@ -1,6 +1,6 @@
/*
* Copyright © 2002 Jorn Baayen
- * Copyright © 2009 Vivien Malerba <malerba nome-db org>
+ * Copyright © 2009 - 2011 Vivien Malerba <malerba nome-db org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -28,6 +28,9 @@ G_BEGIN_DECLS
#define BROWSER_STOCK_COMMIT "transaction-commit"
#define BROWSER_STOCK_ROLLBACK "transaction-rollback"
#define BROWSER_STOCK_BUILDER "glade"
+#define BROWSER_STOCK_LDAP_ENTRIES "ldap-entries"
+#define BROWSER_STOCK_TABLE_ADD "table-add"
+
/* Named icons defined in fd.o Icon Naming Spec */
#define STOCK_NEW_WINDOW "window-new"
diff --git a/tools/browser/browser-window.c b/tools/browser/browser-window.c
index dcb4848..011b029 100644
--- a/tools/browser/browser-window.c
+++ b/tools/browser/browser-window.c
@@ -572,8 +572,11 @@ browser_window_new (BrowserConnection *bcnc, BrowserPerspectiveFactory *factory)
for (plist = browser_core_get_factories (); plist; plist = plist->next) {
GtkAction *action;
const gchar *name;
-
+
name = BROWSER_PERSPECTIVE_FACTORY (plist->data)->perspective_name;
+ if (!strcmp (name, _("LDAP browser")) && !browser_connection_is_ldap (bcnc))
+ continue;
+
action = GTK_ACTION (gtk_radio_action_new (name, name, NULL, NULL, FALSE));
if (!active_action &&
@@ -975,8 +978,8 @@ toolbar_hide_timeout_cb (BrowserWindow *bwin)
return TRUE;
}
-#define BWIN_WINDOW_FULLSCREEN_POPUP_THRESHOLD 5
-#define BWIN_WINDOW_FULLSCREEN_POPUP_TIMER 1
+#define BWIN_WINDOW_FULLSCREEN_POPUP_THRESHOLD 15
+#define BWIN_WINDOW_FULLSCREEN_POPUP_TIMER 5
static gboolean
fullscreen_motion_notify_cb (GtkWidget *widget, GdkEventMotion *event, G_GNUC_UNUSED gpointer user_data)
diff --git a/tools/browser/common/Makefile.am b/tools/browser/common/Makefile.am
index e274295..9b0b787 100644
--- a/tools/browser/common/Makefile.am
+++ b/tools/browser/common/Makefile.am
@@ -1,13 +1,19 @@
noinst_LTLIBRARIES = libcommon.la
+if LDAP
+ldap_flags=-DHAVE_LDAP
+endif
+
AM_CPPFLAGS = \
-I$(top_builddir) \
-I$(top_srcdir) \
-I$(top_srcdir)/libgda \
+ -I$(top_srcdir)/libgda/sqlite \
$(COREDEPS_CFLAGS) \
$(COREDEPS_WFLAGS) \
$(GTK_CFLAGS) \
- $(MAC_INTEGRATION_CFLAGS)
+ $(MAC_INTEGRATION_CFLAGS) \
+ $(ldap_flags)
marshal.h: marshal.list $(GLIB_GENMARSHAL)
$(GLIB_GENMARSHAL) $< --header --prefix=_common_marshal > $@
diff --git a/tools/browser/common/ui-formgrid.c b/tools/browser/common/ui-formgrid.c
index b914a6c..f433616 100644
--- a/tools/browser/common/ui-formgrid.c
+++ b/tools/browser/common/ui-formgrid.c
@@ -27,6 +27,9 @@
#include "../support.h"
#include "../browser-window.h"
#include <libgda/gda-data-model-extra.h>
+#ifdef HAVE_LDAP
+#include "../ldap-browser/ldap-browser-perspective.h"
+#endif
static void ui_formgrid_class_init (UiFormGridClass * class);
static void ui_formgrid_init (UiFormGrid *wid);
@@ -283,7 +286,8 @@ form_grid_toggled_cb (GtkToggleButton *button, UiFormGrid *formgrid)
gtk_button_set_image (GTK_BUTTON (button), gtk_image_new_from_pixbuf (pixbuf));
iter = gdaui_data_selector_get_data_set (GDAUI_DATA_SELECTOR (formgrid->priv->raw_grid));
- row = gda_data_model_iter_get_row (iter);
+ if (iter)
+ row = gda_data_model_iter_get_row (iter);
iter = gdaui_data_selector_get_data_set (GDAUI_DATA_SELECTOR (formgrid->priv->raw_form));
}
else {
@@ -301,11 +305,13 @@ form_grid_toggled_cb (GtkToggleButton *button, UiFormGrid *formgrid)
gtk_button_set_image (GTK_BUTTON (button), gtk_image_new_from_pixbuf (pixbuf));
iter = gdaui_data_selector_get_data_set (GDAUI_DATA_SELECTOR (formgrid->priv->raw_form));
- row = gda_data_model_iter_get_row (iter);
+ if (iter)
+ row = gda_data_model_iter_get_row (iter);
iter = gdaui_data_selector_get_data_set (GDAUI_DATA_SELECTOR (formgrid->priv->raw_grid));
}
- gda_data_model_iter_move_to_row (iter, row >= 0 ? row : 0);
+ if (iter)
+ gda_data_model_iter_move_to_row (iter, row >= 0 ? row : 0);
}
static BrowserConnection *
@@ -325,13 +331,15 @@ get_browser_connection (UiFormGrid *formgrid)
static void execute_action_mitem_cb (GtkMenuItem *menuitem, UiFormGrid *formgrid);
+#ifdef HAVE_LDAP
+static void ldap_view_dn_mitem_cb (GtkMenuItem *menuitem, UiFormGrid *formgrid);
+#endif
static void
form_grid_populate_popup_cb (G_GNUC_UNUSED GtkWidget *wid, GtkMenu *menu, UiFormGrid *formgrid)
{
/* add actions to execute to menu */
GdaDataModelIter *iter;
- GSList *actions_list, *list;
BrowserConnection *bcnc = NULL;
bcnc = get_browser_connection (formgrid);
@@ -339,30 +347,71 @@ form_grid_populate_popup_cb (G_GNUC_UNUSED GtkWidget *wid, GtkMenu *menu, UiForm
return;
iter = gdaui_data_selector_get_data_set (GDAUI_DATA_SELECTOR (formgrid->priv->raw_grid));
+
+ /* actions */
+ GSList *actions_list, *list;
actions_list = browser_favorites_get_actions (browser_connection_get_favorites (bcnc),
bcnc, GDA_SET (iter));
- if (! actions_list)
- return;
-
- GtkWidget *mitem, *submenu;
- mitem = gtk_menu_item_new_with_label (_("Execute action"));
- gtk_widget_show (mitem);
- gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), mitem);
-
- submenu = gtk_menu_new ();
- gtk_menu_item_set_submenu (GTK_MENU_ITEM (mitem), submenu);
- for (list = actions_list; list; list = list->next) {
- BrowserFavoriteAction *act = (BrowserFavoriteAction*) list->data;
- mitem = gtk_menu_item_new_with_label (act->name);
+ if (actions_list) {
+ GtkWidget *mitem, *submenu;
+ mitem = gtk_menu_item_new_with_label (_("Execute action"));
gtk_widget_show (mitem);
- gtk_menu_shell_append (GTK_MENU_SHELL (submenu), mitem);
- g_object_set_data_full (G_OBJECT (mitem), "action", act,
- (GDestroyNotify) browser_favorites_free_action);
- g_signal_connect (mitem, "activate",
- G_CALLBACK (execute_action_mitem_cb), formgrid);
+ gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), mitem);
+
+ submenu = gtk_menu_new ();
+ gtk_menu_item_set_submenu (GTK_MENU_ITEM (mitem), submenu);
+ for (list = actions_list; list; list = list->next) {
+ BrowserFavoriteAction *act = (BrowserFavoriteAction*) list->data;
+ mitem = gtk_menu_item_new_with_label (act->name);
+ gtk_widget_show (mitem);
+ gtk_menu_shell_append (GTK_MENU_SHELL (submenu), mitem);
+ g_object_set_data_full (G_OBJECT (mitem), "action", act,
+ (GDestroyNotify) browser_favorites_free_action);
+ g_signal_connect (mitem, "activate",
+ G_CALLBACK (execute_action_mitem_cb), formgrid);
+ }
+ g_slist_free (actions_list);
}
- g_slist_free (actions_list);
+#ifdef HAVE_LDAP
+ /* LDAP specific */
+ if (browser_connection_is_ldap (bcnc)) {
+ GdaHolder *dnh;
+ dnh = gda_set_get_holder (GDA_SET (iter), "dn");
+ if (dnh) {
+ const GValue *cvalue;
+ cvalue = gda_holder_get_value (GDA_HOLDER (dnh));
+ if (!cvalue && (G_VALUE_TYPE (cvalue) != G_TYPE_STRING))
+ dnh = NULL;
+ }
+ if (!dnh) {
+ GSList *list;
+ for (list = GDA_SET (iter)->holders; list; list = list->next) {
+ const GValue *cvalue;
+ cvalue = gda_holder_get_value (GDA_HOLDER (list->data));
+ if (cvalue && (G_VALUE_TYPE (cvalue) == G_TYPE_STRING) &&
+ gda_ldap_is_dn (g_value_get_string (cvalue))) {
+ dnh = GDA_HOLDER (list->data);
+ break;
+ }
+ }
+ }
+
+ if (dnh) {
+ const GValue *cvalue;
+ cvalue = gda_holder_get_value (dnh);
+
+ GtkWidget *mitem;
+ mitem = gtk_menu_item_new_with_label (_("View LDAP entry's details"));
+ gtk_widget_show (mitem);
+ gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), mitem);
+ g_object_set_data_full (G_OBJECT (mitem), "dn",
+ g_value_dup_string (cvalue), g_free);
+ g_signal_connect (mitem, "activate",
+ G_CALLBACK (ldap_view_dn_mitem_cb), formgrid);
+ }
+ }
+#endif
}
typedef struct {
@@ -594,6 +643,21 @@ execute_action_mitem_cb (GtkMenuItem *menuitem, UiFormGrid *formgrid)
}
}
+#ifdef HAVE_LDAP
+static void ldap_view_dn_mitem_cb (GtkMenuItem *menuitem, UiFormGrid *formgrid)
+{
+ const gchar *dn;
+ BrowserWindow *bwin;
+ BrowserPerspective *pers;
+
+ dn = g_object_get_data (G_OBJECT (menuitem), "dn");
+ bwin = (BrowserWindow*) gtk_widget_get_toplevel ((GtkWidget*) formgrid);
+ pers = browser_window_change_perspective (bwin, _("LDAP browser"));
+
+ ldap_browser_perspective_display_ldap_entry (LDAP_BROWSER_PERSPECTIVE (pers), dn);
+}
+#endif
+
/**
* ui_formgrid_new
* @model: a #GdaDataModel
@@ -626,7 +690,8 @@ ui_formgrid_new (GdaDataModel *model, gboolean scroll_form, GdauiDataProxyInfoFl
GDAUI_DATA_PROXY_INFO_CHUNCK_CHANGE_BUTTONS, NULL);
/* no more than 300 rows at a time */
- gda_data_proxy_set_sample_size (proxy, 300);
+ if (model)
+ gda_data_proxy_set_sample_size (proxy, 300);
return (GtkWidget *) formgrid;
}
diff --git a/tools/browser/data-manager/data-manager-perspective.c b/tools/browser/data-manager/data-manager-perspective.c
index 27f4170..c3c1bc7 100644
--- a/tools/browser/data-manager/data-manager-perspective.c
+++ b/tools/browser/data-manager/data-manager-perspective.c
@@ -36,13 +36,12 @@ static void data_manager_perspective_grab_focus (GtkWidget *widget);
/* BrowserPerspective interface */
static void data_manager_perspective_perspective_init (BrowserPerspectiveIface *iface);
+static BrowserWindow *data_manager_perspective_get_window (BrowserPerspective *perspective);
static GtkActionGroup *data_manager_perspective_get_actions_group (BrowserPerspective *perspective);
static const gchar *data_manager_perspective_get_actions_ui (BrowserPerspective *perspective);
static void data_manager_perspective_get_current_customization (BrowserPerspective *perspective,
GtkActionGroup **out_agroup,
const gchar **out_ui);
-static void adapt_notebook_for_fullscreen (DataManagerPerspective *perspective);
-
/* get a pointer to the parents to be able to call their destructor */
static GObjectClass *parent_class = NULL;
@@ -53,7 +52,6 @@ struct _DataManagerPerspectivePriv {
gboolean favorites_shown;
BrowserWindow *bwin;
BrowserConnection *bcnc;
- gboolean fullscreen;
};
GType
@@ -115,6 +113,7 @@ data_manager_perspective_grab_focus (GtkWidget *widget)
static void
data_manager_perspective_perspective_init (BrowserPerspectiveIface *iface)
{
+ iface->i_get_window = data_manager_perspective_get_window;
iface->i_get_actions_group = data_manager_perspective_get_actions_group;
iface->i_get_actions_ui = data_manager_perspective_get_actions_ui;
iface->i_get_current_customization = data_manager_perspective_get_current_customization;
@@ -126,20 +125,12 @@ data_manager_perspective_init (DataManagerPerspective *perspective)
{
perspective->priv = g_new0 (DataManagerPerspectivePriv, 1);
perspective->priv->favorites_shown = TRUE;
- perspective->priv->fullscreen = FALSE;
}
static void fav_selection_changed_cb (GtkWidget *widget, gint fav_id, BrowserFavoritesType fav_type,
const gchar *selection, DataManagerPerspective *perspective);
-static void nb_switch_page_cb (GtkNotebook *nb, GtkWidget *page, gint page_num,
- DataManagerPerspective *perspective);
-static void nb_page_removed_cb (GtkNotebook *nb, GtkWidget *page, gint page_num,
- DataManagerPerspective *perspective);
-static void nb_page_added_cb (GtkNotebook *nb, GtkWidget *page, gint page_num,
- DataManagerPerspective *perspective);
static void close_button_clicked_cb (GtkWidget *wid, GtkWidget *page_widget);
-static void fullscreen_changed_cb (BrowserWindow *bwin, gboolean fullscreen, DataManagerPerspective *perspective);
/**
* data_manager_perspective_new
@@ -152,37 +143,32 @@ data_manager_perspective_new (BrowserWindow *bwin)
BrowserConnection *bcnc;
BrowserPerspective *bpers;
DataManagerPerspective *perspective;
+ gboolean fav_supported;
+
bpers = (BrowserPerspective*) g_object_new (TYPE_DATA_MANAGER_PERSPECTIVE, NULL);
perspective = (DataManagerPerspective*) bpers;
-
perspective->priv->bwin = bwin;
- g_signal_connect (bwin, "fullscreen-changed",
- G_CALLBACK (fullscreen_changed_cb), bpers);
bcnc = browser_window_get_connection (bwin);
perspective->priv->bcnc = g_object_ref (bcnc);
- perspective->priv->fullscreen = browser_window_is_fullscreen (bwin);
+ fav_supported = browser_connection_get_favorites (bcnc) ? TRUE : FALSE;
/* contents */
GtkWidget *paned, *nb, *wid;
paned = gtk_hpaned_new ();
- wid = data_favorite_selector_new (bcnc);
- g_signal_connect (wid, "selection-changed",
- G_CALLBACK (fav_selection_changed_cb), bpers);
- gtk_paned_pack1 (GTK_PANED (paned), wid, FALSE, TRUE);
- gtk_paned_set_position (GTK_PANED (paned), DEFAULT_FAVORITES_SIZE);
- perspective->priv->favorites = wid;
+ if (fav_supported) {
+ wid = data_favorite_selector_new (bcnc);
+ g_signal_connect (wid, "selection-changed",
+ G_CALLBACK (fav_selection_changed_cb), bpers);
+ gtk_paned_pack1 (GTK_PANED (paned), wid, FALSE, TRUE);
+ gtk_paned_set_position (GTK_PANED (paned), DEFAULT_FAVORITES_SIZE);
+ perspective->priv->favorites = wid;
+ }
nb = gtk_notebook_new ();
perspective->priv->notebook = nb;
gtk_paned_pack2 (GTK_PANED (paned), nb, TRUE, TRUE);
gtk_notebook_set_scrollable (GTK_NOTEBOOK (nb), TRUE);
gtk_notebook_popup_enable (GTK_NOTEBOOK (nb));
- g_signal_connect (G_OBJECT (nb), "switch-page",
- G_CALLBACK (nb_switch_page_cb), perspective);
- g_signal_connect (G_OBJECT (nb), "page-removed",
- G_CALLBACK (nb_page_removed_cb), perspective);
- g_signal_connect (G_OBJECT (nb), "page-added",
- G_CALLBACK (nb_page_added_cb), perspective);
GtkWidget *page, *tlabel, *button;
page = data_console_new (bcnc);
@@ -203,13 +189,12 @@ data_manager_perspective_new (BrowserWindow *bwin)
gtk_box_pack_start (GTK_BOX (bpers), paned, TRUE, TRUE, 0);
gtk_widget_show_all (paned);
- if (!perspective->priv->favorites_shown)
+ if (perspective->priv->favorites && !perspective->priv->favorites_shown)
gtk_widget_hide (perspective->priv->favorites);
gtk_widget_grab_focus (page);
- if (perspective->priv->fullscreen)
- adapt_notebook_for_fullscreen (perspective);
+ browser_perspective_declare_notebook (bpers, GTK_NOTEBOOK (perspective->priv->notebook));
return bpers;
}
@@ -239,7 +224,6 @@ add_new_data_console (BrowserPerspective *bpers, gint fav_id)
tlabel = browser_page_get_tab_label (BROWSER_PAGE (page), NULL);
gtk_notebook_set_menu_label (GTK_NOTEBOOK (perspective->priv->notebook), page, tlabel);
- adapt_notebook_for_fullscreen (perspective);
return DATA_CONSOLE (page);
}
@@ -296,69 +280,12 @@ fav_selection_changed_cb (G_GNUC_UNUSED GtkWidget *widget, gint fav_id, BrowserF
}
static void
-nb_switch_page_cb (GtkNotebook *nb, G_GNUC_UNUSED GtkWidget *page, gint page_num,
- DataManagerPerspective *perspective)
-{
- GtkWidget *page_contents;
- GtkActionGroup *actions = NULL;
- const gchar *ui = NULL;
-
- page_contents = gtk_notebook_get_nth_page (nb, page_num);
- if (IS_BROWSER_PAGE (page_contents)) {
- actions = browser_page_get_actions_group (BROWSER_PAGE (page_contents));
- ui = browser_page_get_actions_ui (BROWSER_PAGE (page_contents));
- }
- browser_window_customize_perspective_ui (perspective->priv->bwin,
- BROWSER_PERSPECTIVE (perspective), actions,
- ui);
- if (actions)
- g_object_unref (actions);
-}
-
-static void
-nb_page_removed_cb (GtkNotebook *nb, G_GNUC_UNUSED GtkWidget *page, G_GNUC_UNUSED gint page_num,
- DataManagerPerspective *perspective)
-{
- if (gtk_notebook_get_n_pages (nb) == 0) {
- browser_window_customize_perspective_ui (perspective->priv->bwin,
- BROWSER_PERSPECTIVE (perspective),
- NULL, NULL);
- }
- adapt_notebook_for_fullscreen (perspective);
-}
-
-static void
-nb_page_added_cb (G_GNUC_UNUSED GtkNotebook *nb, G_GNUC_UNUSED GtkWidget *page,
- G_GNUC_UNUSED gint page_num, DataManagerPerspective *perspective)
-{
- adapt_notebook_for_fullscreen (perspective);
-}
-
-static void
close_button_clicked_cb (G_GNUC_UNUSED GtkWidget *wid, GtkWidget *page_widget)
{
gtk_widget_destroy (page_widget);
}
static void
-adapt_notebook_for_fullscreen (DataManagerPerspective *perspective)
-{
- gboolean showtabs = TRUE;
-
- if (perspective->priv->fullscreen &&
- gtk_notebook_get_n_pages (GTK_NOTEBOOK (perspective->priv->notebook)) == 1)
- showtabs = FALSE;
- gtk_notebook_set_show_tabs (GTK_NOTEBOOK (perspective->priv->notebook), showtabs);
-}
-
-static void
-fullscreen_changed_cb (G_GNUC_UNUSED BrowserWindow *bwin, gboolean fullscreen, DataManagerPerspective *perspective)
-{
- perspective->priv->fullscreen = fullscreen;
- adapt_notebook_for_fullscreen (perspective);
-}
-
-static void
data_manager_perspective_dispose (GObject *object)
{
DataManagerPerspective *perspective;
@@ -368,15 +295,10 @@ data_manager_perspective_dispose (GObject *object)
perspective = DATA_MANAGER_PERSPECTIVE (object);
if (perspective->priv) {
+ browser_perspective_declare_notebook ((BrowserPerspective*) perspective, NULL);
if (perspective->priv->bcnc)
g_object_unref (perspective->priv->bcnc);
- g_signal_handlers_disconnect_by_func (perspective->priv->notebook,
- G_CALLBACK (nb_page_removed_cb), perspective);
- g_signal_handlers_disconnect_by_func (perspective->priv->notebook,
- G_CALLBACK (nb_page_added_cb), perspective);
- g_signal_handlers_disconnect_by_func (perspective->priv->notebook,
- G_CALLBACK (nb_switch_page_cb), perspective);
g_free (perspective->priv);
perspective->priv = NULL;
}
@@ -396,6 +318,9 @@ favorites_toggle_cb (GtkToggleAction *action, BrowserPerspective *bpers)
{
DataManagerPerspective *perspective;
perspective = DATA_MANAGER_PERSPECTIVE (bpers);
+
+ if (! perspective->priv->favorites)
+ return;
perspective->priv->favorites_shown = gtk_toggle_action_get_active (action);
if (perspective->priv->favorites_shown)
gtk_widget_show (perspective->priv->favorites);
@@ -433,18 +358,24 @@ static const gchar *ui_actions_info =
"</ui>";
static GtkActionGroup *
-data_manager_perspective_get_actions_group (BrowserPerspective *bpers)
+data_manager_perspective_get_actions_group (BrowserPerspective *perspective)
{
GtkActionGroup *agroup;
+ DataManagerPerspective *bpers;
+ bpers = DATA_MANAGER_PERSPECTIVE (perspective);
agroup = gtk_action_group_new ("DataManagerActions");
gtk_action_group_set_translation_domain (agroup, GETTEXT_PACKAGE);
gtk_action_group_add_actions (agroup, ui_actions, G_N_ELEMENTS (ui_actions), bpers);
- gtk_action_group_add_toggle_actions (agroup, ui_toggle_actions, G_N_ELEMENTS (ui_toggle_actions), bpers);
+ gtk_action_group_add_toggle_actions (agroup, ui_toggle_actions,
+ G_N_ELEMENTS (ui_toggle_actions), bpers);
GtkAction *action;
action = gtk_action_group_get_action (agroup, "DataManagerFavoritesShow");
- gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
- DATA_MANAGER_PERSPECTIVE (bpers)->priv->favorites_shown);
+ if (bpers->priv->favorites)
+ gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
+ bpers->priv->favorites_shown);
+ else
+ gtk_action_set_sensitive (GTK_ACTION (action), FALSE);
return agroup;
}
@@ -511,3 +442,11 @@ data_manager_perspective_get_current_customization (BrowserPerspective *perspect
*out_ui = browser_page_get_actions_ui (BROWSER_PAGE (page_contents));
}
}
+
+static BrowserWindow *
+data_manager_perspective_get_window (BrowserPerspective *perspective)
+{
+ DataManagerPerspective *bpers;
+ bpers = DATA_MANAGER_PERSPECTIVE (perspective);
+ return bpers->priv->bwin;
+}
diff --git a/tools/browser/data/Makefile.am b/tools/browser/data/Makefile.am
index ae0df1f..85da07a 100644
--- a/tools/browser/data/Makefile.am
+++ b/tools/browser/data/Makefile.am
@@ -20,9 +20,12 @@ private_icons = \
hicolor_actions_24x24_transaction-commit.png \
hicolor_actions_24x24_transaction-rollback.png \
hicolor_actions_24x24_glade.png \
+ hicolor_actions_24x24_table-add.png \
hicolor_actions_32x32_bookmark-view.png \
hicolor_actions_32x32_history-view.png \
hicolor_actions_32x32_glade.png \
+ hicolor_actions_32x32_ldap-entries.png \
+ hicolor_actions_32x32_table-add.png \
hicolor_actions_scalable_bookmark-view.svg \
hicolor_actions_scalable_history-view.svg \
hicolor_actions_scalable_glade.svg \
diff --git a/tools/browser/data/hicolor_actions_24x24_table-add.png b/tools/browser/data/hicolor_actions_24x24_table-add.png
new file mode 100644
index 0000000..352e7b3
Binary files /dev/null and b/tools/browser/data/hicolor_actions_24x24_table-add.png differ
diff --git a/tools/browser/data/hicolor_actions_32x32_ldap-entries.png b/tools/browser/data/hicolor_actions_32x32_ldap-entries.png
new file mode 100644
index 0000000..7b47f46
Binary files /dev/null and b/tools/browser/data/hicolor_actions_32x32_ldap-entries.png differ
diff --git a/tools/browser/data/hicolor_actions_32x32_table-add.png b/tools/browser/data/hicolor_actions_32x32_table-add.png
new file mode 100644
index 0000000..40ea139
Binary files /dev/null and b/tools/browser/data/hicolor_actions_32x32_table-add.png differ
diff --git a/tools/browser/decl.h b/tools/browser/decl.h
index 09eb70b..0719d3e 100644
--- a/tools/browser/decl.h
+++ b/tools/browser/decl.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009 - 2010 The GNOME Foundation.
+ * Copyright (C) 2009 - 2011 The GNOME Foundation.
*
* AUTHORS:
* Vivien Malerba <malerba gnome-db org>
@@ -45,10 +45,6 @@ typedef struct {
} BrowserPerspectiveFactory;
#define BROWSER_PERSPECTIVE_FACTORY(x) ((BrowserPerspectiveFactory*)(x))
-#define ORDER_KEY_SCHEMA 1
-#define ORDER_KEY_QUERIES 2
-#define ORDER_KEY_DATA_MANAGERS 3
-
#define DEFAULT_FAVORITES_SIZE 150
#define DEFAULT_DATA_SELECT_LIMIT 500
diff --git a/tools/browser/doc/Makefile.am b/tools/browser/doc/Makefile.am
index 50d3e25..971fa79 100644
--- a/tools/browser/doc/Makefile.am
+++ b/tools/browser/doc/Makefile.am
@@ -40,19 +40,25 @@ GTKDOC_CFLAGS = -I$(top_srcdir) \
$(GTKSOURCEVIEW_CFLAGS) \
-DGETTEXT_PACKAGE=\""$(GETTEXT_PACKAGE)"\"
-ADDLIBS=
+ADDCANVASLIBS=
if HAVE_GOOCANVAS
-ADDLIBS+=$(top_builddir)/tools/browser/canvas/libcanvas.la
+ADDCANVASLIBS+=$(top_builddir)/tools/browser/canvas/libcanvas.la
+endif
+
+ADDLDAPLIBS=
+if LDAP
+ADDLDAPLIBS+=$(top_builddir)/tools/browser/ldap-browser/libperspective.la
endif
GTKDOC_LIBS = \
$(top_builddir)/tools/browser/data-manager/libperspective.la \
$(top_builddir)/tools/browser/schema-browser/libperspective.la \
- $(ADDLIBS) \
+ $(ADDCANVASLIBS) \
$(top_builddir)/tools/browser/query-exec/libperspective.la \
$(top_builddir)/tools/browser/libbrowser.la \
$(top_builddir)/libgda-ui/internal/libgda-ui-internal.la \
$(top_builddir)/tools/browser/common/libcommon.la \
+ $(ADDLDAPLIBS) \
$(top_builddir)/libgda/libgda-5.0.la \
$(top_builddir)/libgda-ui/libgda-ui-5.0.la \
$(COREDEPS_LIBS) \
diff --git a/tools/browser/doc/gda-browser-sections.txt b/tools/browser/doc/gda-browser-sections.txt
index 7ffd91d..ffc8ad8 100644
--- a/tools/browser/doc/gda-browser-sections.txt
+++ b/tools/browser/doc/gda-browser-sections.txt
@@ -128,6 +128,7 @@ BrowserFavoritesAttributes
<TITLE>BrowserFavorites</TITLE>
BrowserFavorites
browser_favorites_new
+browser_favorites_type_to_string
browser_favorites_add
browser_favorites_list
browser_favorites_delete
@@ -156,10 +157,13 @@ IS_BROWSER_PERSPECTIVE
BROWSER_PERSPECTIVE_GET_CLASS
BrowserPerspective
browser_perspective_get_type
+browser_perspective_get_window
browser_perspective_get_actions_group
browser_perspective_get_actions_ui
browser_perspective_get_current_customization
browser_perspective_page_tab_label_change
+<SUBSECTION>
+browser_perspective_declare_notebook
</SECTION>
<SECTION>
diff --git a/tools/browser/dummy-perspective/dummy-perspective.c b/tools/browser/dummy-perspective/dummy-perspective.c
index cf8a247..02e5d65 100644
--- a/tools/browser/dummy-perspective/dummy-perspective.c
+++ b/tools/browser/dummy-perspective/dummy-perspective.c
@@ -25,11 +25,12 @@
* Main static functions
*/
static void dummy_perspective_class_init (DummyPerspectiveClass *klass);
-static void dummy_perspective_init (DummyPerspective *stmt);
+static void dummy_perspective_init (DummyPerspective *pers);
static void dummy_perspective_dispose (GObject *object);
/* BrowserPerspective interface */
static void dummy_perspective_perspective_init (BrowserPerspectiveIface *iface);
+static BrowserWindow *dummy_perspective_get_window (BrowserPerspective *perspective);
static GtkActionGroup *dummy_perspective_get_actions_group (BrowserPerspective *perspective);
static const gchar *dummy_perspective_get_actions_ui (BrowserPerspective *perspective);
/* get a pointer to the parents to be able to call their destructor */
@@ -84,6 +85,7 @@ dummy_perspective_class_init (DummyPerspectiveClass * klass)
static void
dummy_perspective_perspective_init (BrowserPerspectiveIface *iface)
{
+ iface->i_get_window = dummy_perspective_get_window;
iface->i_get_actions_group = dummy_perspective_get_actions_group;
iface->i_get_actions_ui = dummy_perspective_get_actions_ui;
}
@@ -113,6 +115,10 @@ dummy_perspective_new (G_GNUC_UNUSED BrowserWindow *bwin)
BrowserPerspective *bpers;
bpers = (BrowserPerspective*) g_object_new (TYPE_DUMMY_PERSPECTIVE, NULL);
+ /* if there is a notebook to store pages, use:
+ browser_perspective_declare_notebook (bpers, GTK_NOTEBOOK (perspective->priv->notebook));
+ */
+
return bpers;
}
@@ -126,6 +132,7 @@ dummy_perspective_dispose (GObject *object)
g_return_if_fail (IS_DUMMY_PERSPECTIVE (object));
perspective = DUMMY_PERSPECTIVE (object);
+ browser_perspective_declare_notebook ((BrowserPerspective*) perspective, NULL);
/* parent class */
parent_class->dispose (object);
@@ -184,3 +191,11 @@ dummy_perspective_get_actions_ui (G_GNUC_UNUSED BrowserPerspective *bpers)
{
return ui_actions_info;
}
+
+static BrowserWindow *
+dummy_perspective_get_window (BrowserPerspective *perspective)
+{
+ DummyPerspective *bpers;
+ bpers = DUMMY_PERSPECTIVE (perspective);
+ return NULL;/*bpers->priv->bwin;*/
+}
diff --git a/tools/browser/gda-browser-ldap-class-a.png b/tools/browser/gda-browser-ldap-class-a.png
new file mode 100644
index 0000000..1521a5b
Binary files /dev/null and b/tools/browser/gda-browser-ldap-class-a.png differ
diff --git a/tools/browser/gda-browser-ldap-class-s.png b/tools/browser/gda-browser-ldap-class-s.png
new file mode 100644
index 0000000..0f2b630
Binary files /dev/null and b/tools/browser/gda-browser-ldap-class-s.png differ
diff --git a/tools/browser/gda-browser-ldap-class-u.png b/tools/browser/gda-browser-ldap-class-u.png
new file mode 100644
index 0000000..3dca45a
Binary files /dev/null and b/tools/browser/gda-browser-ldap-class-u.png differ
diff --git a/tools/browser/gda-browser-ldap-class-x.png b/tools/browser/gda-browser-ldap-class-x.png
new file mode 100644
index 0000000..ba9c76c
Binary files /dev/null and b/tools/browser/gda-browser-ldap-class-x.png differ
diff --git a/tools/browser/gda-browser-ldap-entry.png b/tools/browser/gda-browser-ldap-entry.png
new file mode 100644
index 0000000..55ea0c6
Binary files /dev/null and b/tools/browser/gda-browser-ldap-entry.png differ
diff --git a/tools/browser/gda-browser-ldap-group.png b/tools/browser/gda-browser-ldap-group.png
new file mode 100644
index 0000000..a14a75b
Binary files /dev/null and b/tools/browser/gda-browser-ldap-group.png differ
diff --git a/tools/browser/gda-browser-ldap-organization.png b/tools/browser/gda-browser-ldap-organization.png
new file mode 100644
index 0000000..f3fb913
Binary files /dev/null and b/tools/browser/gda-browser-ldap-organization.png differ
diff --git a/tools/browser/gda-browser-ldap-person.png b/tools/browser/gda-browser-ldap-person.png
new file mode 100644
index 0000000..397cd73
Binary files /dev/null and b/tools/browser/gda-browser-ldap-person.png differ
diff --git a/tools/browser/help/C/features.page b/tools/browser/help/C/features.page
index 9019a20..c8b0a5a 100644
--- a/tools/browser/help/C/features.page
+++ b/tools/browser/help/C/features.page
@@ -39,6 +39,8 @@
<link xref="query-execution-perspective">Query execution perspective</link></p></item>
<item><p>analyse the table's contents, see the
<link xref="data-manager-perspective">Data manager perspective</link></p></item>
+ <item><p>for <link xref="ldap-connection">LDAP connections</link>, manage the hierarchical data in the LDAP tree, see the
+ <link xref="ldap-browser-perspective">LDAP browser perspective</link></p></item>
</list>
<p>
See also the section about <link xref="actions">actions</link>
diff --git a/tools/browser/help/C/figures/ldap-browser-persp.png b/tools/browser/help/C/figures/ldap-browser-persp.png
new file mode 100644
index 0000000..50d5434
Binary files /dev/null and b/tools/browser/help/C/figures/ldap-browser-persp.png differ
diff --git a/tools/browser/help/C/figures/ldap-classes.png b/tools/browser/help/C/figures/ldap-classes.png
new file mode 100644
index 0000000..f0c3a2d
Binary files /dev/null and b/tools/browser/help/C/figures/ldap-classes.png differ
diff --git a/tools/browser/help/C/figures/ldap-search.png b/tools/browser/help/C/figures/ldap-search.png
new file mode 100644
index 0000000..96752b0
Binary files /dev/null and b/tools/browser/help/C/figures/ldap-search.png differ
diff --git a/tools/browser/help/C/figures/ldap-table-mapping.png b/tools/browser/help/C/figures/ldap-table-mapping.png
new file mode 100644
index 0000000..dd36e32
Binary files /dev/null and b/tools/browser/help/C/figures/ldap-table-mapping.png differ
diff --git a/tools/browser/help/C/index.page b/tools/browser/help/C/index.page
index 5db715c..8a1f5f2 100644
--- a/tools/browser/help/C/index.page
+++ b/tools/browser/help/C/index.page
@@ -20,7 +20,8 @@
for which a database driver (provider) exists in <app>libgda</app>
(<link href="http://www.oracle.com">Oracle</link>, <link href="http://www.mysql.org">MySQL</link>,
<link href="http://www.postgresql.org/">PostgreSQL</link>, <link href="http://www.sqlite.org">SQLite</link>,
- MS Access (through the <link href="http://sourceforge.net/projects/mdbtools/">MDBTools</link> library) and
+ MS Access (through the <link href="http://sourceforge.net/projects/mdbtools/">MDBTools</link> library),
+ <link href="http://en.wikipedia.org/wiki/LDAP">LDAP</link> and
<link href="http://java.sun.com/docs/books/tutorial/jdbc/index.html">JDBC</link> are supported at
the moment). </p>
diff --git a/tools/browser/help/C/ldap-browser-perspective.page b/tools/browser/help/C/ldap-browser-perspective.page
new file mode 100644
index 0000000..5c50841
--- /dev/null
+++ b/tools/browser/help/C/ldap-browser-perspective.page
@@ -0,0 +1,61 @@
+<page xmlns="http://projectmallard.org/1.0/"
+ type="topic"
+ id="ldap-browser-perspective">
+<info>
+ <title type="sort">1</title>
+ <link type="guide" xref="index#perspectives"/>
+</info>
+<title>The LDAP browser perspective</title>
+<p>
+ Use the LDAP browser perspective to view and manipulate data stored in an LDAP directory. To switch
+ to this perspective, use the <guiseq><gui>Perspective</gui><gui>LDAP browser</gui></guiseq> menu, or
+ the <keyseq><key>Ctrl</key><key>P</key></keyseq> shortcut. This perspective is of course only available when the opened connection is an <link xref="ldap-connection">LDAP connection</link>.
+</p>
+<p>
+ The perspective is divided in two horizontal panes: the left pane for the user defined
+ favorites (to hold references to specific LDAP entries or specific LDAP classes), and
+ the right pane being the action area.
+</p>
+<figure>
+ <title>LDAP browser's entry tab</title>
+ <desc>LDAP entries tab</desc>
+ <media type="image" mime="image/png" src="figures/ldap-browser-persp.png"/>
+</figure>
+
+<p>
+ The left
+ part of the perspective lists the favorite LDAP entries or classes. Double clicking on a favorite
+ opens its properties in the right pane. Note that the LDAP entries favorites will always appear
+ before the classes favorites.
+</p>
+<p>
+ The right pane is composed of several types of tabs:
+</p>
+<list>
+ <item><p>tabs to explore the LDAP DIT (Directory Information Tree): when an entry is selected from the
+ tree, its attributes are displayed (only the valued attributes are displayed, the ones with no value
+ are hidden), and the entry's DN (Distinguished Name) is always displayed first. Also note that the children
+ of each entry are only fetched when necessary to avoid unnecessary requests to the LDAP server.</p></item>
+ <item><p>tabs to explore the LDAP's classes, see figure below. For a selected class, all the information
+ regarding the class is displayed (description, OID, type, ...)</p></item>
+ <item><p>tabs to perform LDAP searches, see figure below. An LDAP search definition can be saved as a virtual
+ table, see the <link xref="ldap-connection#ldap-table-mapping">LDAP table's mapping</link>.</p></item>
+</list>
+<figure>
+ <title>LDAP browser's classes tab</title>
+ <desc>LDAP classes tab</desc>
+ <media type="image" mime="image/png" src="figures/ldap-classes.png"/>
+</figure>
+<figure>
+ <title>LDAP browser's search tab</title>
+ <desc>LDAP search tab</desc>
+ <media type="image" mime="image/png" src="figures/ldap-search.png"/>
+</figure>
+<p>
+ Links in these tabs (identified by blue and underlined text) open a new tab, or use the first tab next
+ to the current tab to display information about the selected link. For example in the figure above illustrating
+ an LDAP entries tab, clicking on
+ the "inetOrgPerson" will open a new "LDAP classes" tab and disply the information about that class.
+</p>
+
+</page>
diff --git a/tools/browser/help/C/ldap-connections.page b/tools/browser/help/C/ldap-connections.page
new file mode 100644
index 0000000..234e446
--- /dev/null
+++ b/tools/browser/help/C/ldap-connections.page
@@ -0,0 +1,132 @@
+<page xmlns="http://projectmallard.org/1.0/"
+ type="topic"
+ id="ldap-connection">
+ <info>
+ <title type="sort">1</title>
+ <link type="guide" xref="features"/>
+ <link type="guide" xref="index#connections"/>
+ </info>
+ <title>LDAP connections</title>
+ <p>
+ LDAP connections are different than other connections in a way that an LDAP database stores data in
+ a hierarchical way, in the DIT (Directory Information Tree), whereas other databases acessible using
+ the <app>gda-browser</app> application are relational databases.
+ </p>
+ <p>
+ As a consequence, LDAP connections are treaded specially: as normal connections with tables (see the
+ table mapping explained next), and through the presence of the <link xref="ldap-browser-perspective">
+ LDAP browser perspective</link>
+ </p>
+ <p>
+ Note that LDAP connections may not be available is either the LDAP database provider is not installed
+ or if the LDAP support was disabled during the compilation.
+ </p>
+
+ <section id="ldap-table-mapping">
+ <title>LDAP table's mapping</title>
+ <p>
+ Within an LDAP connection, it is possible to declare virtual tables which are mapped to an LDAP search.
+ These virtual tables can then later be used like any other table. The first column of each LDAP virtual
+ table will always be the DN (Distinguished Name) of the entry represented in the row; the other columns
+ depend on the table definition.
+ </p>
+ <p>
+ An LDAP virtual table is defined by the following attributes:
+ </p>
+ <list>
+ <item><p>a table name</p></item>
+ <item><p>the base DN for the search: the LDAP entry at which the search begins (if not specified
+ then the top level entry of the LDAP connection is used)</p></item>
+ <item><p>the search filter: a valid LDAP search filter (if none is provided then the default
+ search filter is "(objectClass=*)", requesting any LDAP entry).</p></item>
+ <item><p>the attributes: the attributes to retreive, each attribute will be mapped to a column of the
+ table. The attributes must be a comma separated list of attributes, where each attribute can optionally
+ be assigned a type and a multi value option (see below).</p></item>
+ <item><p>the scope: the search scope, "subtree" (to search the base DN and the entire sub tree below),
+ "onelevel" (to search the immediate children of the base DN entry only), or
+ "base" (to search the base DN only)</p></item>
+ </list>
+ <figure>
+ <title>LDAP table's properties</title>
+ <desc>LDAP table's properties</desc>
+ <media type="image" mime="image/png" src="figures/ldap-table-mapping.png"/>
+ </figure>
+ <p>
+ For example in the figure above, the "users" table will "contain" all the LDAP entries from
+ the top level LDAP entry of the connection, and have 3 columns: the DN, the "cn" and the "jpegPhoto".
+ </p>
+ </section>
+
+ <section id="ldap-columns-mapping">
+ <title>Attributes to columns mapping</title>
+ <p>
+ As mentionned in the previous section, each attribute will be mapped to a column. The column type
+ is normally automatically determined (string, number, ...) but can be forced by appending to the attribute
+ name the "::<type>" for the requested type.
+ </p>
+ <p>
+ Also, because
+ some attributes can have multiple values, the table construction handles multi-valued attributes in
+ different ways, depending on each attribute's options, an option can be specified by appending the
+ "::<option>" to the attribute name. Valid options are:
+ </p>
+ <list>
+ <item><p>"NULL" or "0": a NULL value will be returned for the attribute</p></item>
+ <item><p>"CSV": a comma separated value with all the values of the attribute will be returned.
+ This only works for string attribute types.</p></item>
+ <item><p>"MULT" or "*": a row will be returned for each value of the attribute, effectively
+ multiplying the number of returned rows</p></item>
+ <item><p>"1": only the first vakue of the attribute will be used, the other values ignored</p></item>
+ <item><p>"CONCAT": the attributes' values are concatenated (with a newline char
+ between each value)</p></item>
+ <item><p>"ERROR": an error value will be returned (this is the default behaviour)</p></item>
+ </list>
+ </section>
+
+ <section id="ldap-ddl-sql">
+ <title>SQL useable with LDAP connections</title>
+ <p>
+ You can use the SQL understood by <link href="http://sqlite.org/lang.html">SQLite</link> in any LDAP
+ connection. Be aware however that if you define database objects (outside of the extended SQL
+ presented next section), they will be lost the next time the LDAP connection is opened.
+ </p>
+ <p>
+ So it is perfectly safe for example to create a table to store some LDAP data which
+ may require a long operation to obtain, but after closing the LDAP connection, the table
+ and its data will be lost.
+ </p>
+ <p>
+ See the <link xref="sql-sqlite">SQL understood by LDAP connections and virtual connections</link>
+ section for more information.
+ </p>
+ </section>
+
+ <section id="ldap-sql">
+ <title>SQL extension to hande LDAP tables</title>
+ <p>
+ LDAP tables can be created using an extended set of SQL commands:
+ </p>
+ <list>
+ <item><p><code>CREATE LDAP TABLE <table_name> [BASE='<base_dn>'] [FILTER='<filter>'] [ATTRIBUTES='<filter>'] [SCOPE='<filter>']</code> to declare a new LDAP virtual table</p></item>
+ <item><p><code>DESCRIBE LDAP TABLE <table_name></code> to show LDAP virtual table's definition</p></item>
+ <item><p><code>ALTER LDAP TABLE <table_name> [BASE='<base_dn>'] [FILTER='<filter>'] [ATTRIBUTES='<filter>'] [SCOPE='<filter>']</code> to modify an LDAP virtual table's definition (only the specified part is actually modified</p></item>
+ <item><p><code>DROP LDAP TABLE <table_name></code> to remove an LDAP virtual table. Note that the
+ usual <code>DROP TABLE <table_name></code> can also be used instead.</p></item>
+ </list>
+ <p>
+ For example the following SQL code:
+ </p>
+ <code>
+ CREATE LDAP TABLE users FILTER='(objectClass=inetOrgPerson)'
+ ATTRIBUTES='cn,sn,givenName,seeAlso::*' SCOPE='subtree';
+ SELECT * FROM users WHERE cn like '%john%';
+ ALTER LDAP TABLE users FILTER='(&(objectClass=inetOrgPerson)(cn=*john*))';
+ SELECT * FROM users;
+ DROP LDAP TABLE users;
+ </code>
+ <p>
+ should display twice the same results, which list all the LDAP entries of the "inetOrgPerson" class with
+ a CommonName (cn) containing the "john" string.
+ </p>
+ </section>
+</page>
diff --git a/tools/browser/help/C/sql-sqlite.page b/tools/browser/help/C/sql-sqlite.page
new file mode 100644
index 0000000..4f92bb1
--- /dev/null
+++ b/tools/browser/help/C/sql-sqlite.page
@@ -0,0 +1,100 @@
+<page xmlns="http://projectmallard.org/1.0/"
+ type="topic"
+ id="sql-sqlite">
+ <info>
+ <title type="sort">1</title>
+ </info>
+ <title>SQL understood by LDAP connections and virtual connections</title>
+ <p>
+ Both virtual connections in <app>gda-browser</app> and LDAP connections internally rely on
+ <link href="http://www.sqlite.org/vtab.html">SQLite's virtual tables</link> mechanism, and
+ as a consequence, the SQL to use in these connections is the same and the limitations
+ are the same.
+ </p>
+ <p>
+ All the SQL understood by <link href="http://sqlite.org/lang.html">SQLite</link> is useable
+ in these connections, with the limitations and modifications detailled in the next sections.
+ </p>
+
+ <section id="sql-sqlite-dbobjects">
+ <title>Database objects</title>
+ <p>
+ It is possible to create database objects (tables, views, ...) but these will be lost when the
+ connection is closed. The only "persistant" objects are the ones automatically created
+ when the connection is opened (and they contain no data as they are merely proxies to
+ other sources of data).
+ </p>
+ </section>
+
+ <section id="sql-sqlite-extrafunctions">
+ <title>Extra available functions</title>
+ <p>
+ All the functions provided by <link href="http://sqlite.org/lang_corefunc.html">SQLite</link>
+ are useable in these connections, and a few other ones have been added:
+ </p>
+ <list>
+ <item>
+ <p>
+ the <cmd>gda_file_exists()</cmd> function accepts a filename as argument,
+ and returns 0 if the file with that filename does not
+ exist, or 1 if it does.
+ </p>
+ </item>
+
+ <item>
+ <p>
+ the <cmd>gda_hex_print()</cmd> function
+ accepts at most 2 arguments, in that order:
+ </p>
+ <list>
+ <item><p>a blob value</p></item>
+ <item><p>a length (not mandatory)</p></item>
+ </list>
+ <p>
+ It returns a string suitable to be printed (where all the non ascii characters are converted to
+ the "\xyz" syntax where "xyz" is the decimal value of the character), limited to the specified
+ length if any.
+ </p>
+ </item>
+
+
+ <item>
+ <p>
+ the <cmd>gda_hex()</cmd> function
+ accepts at most 2 arguments, in that order:
+ </p>
+ <list>
+ <item><p>a blob value</p></item>
+ <item><p>a length (not mandatory)</p></item>
+ </list>
+ <p>It returns a hex dump string of the blob value, limited to the specified length if any</p>
+ </item>
+
+ <item>
+ <p>
+ the <cmd>gda_rmdiacr()</cmd> function
+ accepts at most 2 arguments, in that order:
+ </p>
+ <list>
+ <item><p>a string value</p></item>
+ <item><p>a case conversion to do (not mandatory), as a string which must be
+ 'upper' or 'lower'</p></item>
+ </list>
+ <p>
+ It returns a string where all the diacritical signs (for example accents) from the input string,
+ and optionally converts the string to upper or lower case if specified. This function takes into
+ account the current locale and is useful to do some text search.
+ </p>
+ </item>
+
+ <item>
+ <p>
+ the <cmd>gda_upper()</cmd> and <cmd>gda_lower()</cmd> functions
+ accept one string argument and convert it to upper or lower case, taking into account
+ the locale (the standard SQLite <cmd>upper()</cmd> and <cmd>lower()</cmd>
+ functions only operating on ASCII characters).
+ </p>
+ </item>
+ </list>
+ </section>
+</page>
diff --git a/tools/browser/help/C/virtual-connections.page b/tools/browser/help/C/virtual-connections.page
index 45a0a7e..2afaf22 100644
--- a/tools/browser/help/C/virtual-connections.page
+++ b/tools/browser/help/C/virtual-connections.page
@@ -123,6 +123,18 @@ WHERE "Error code"=0
<title>New opened virtual connection</title>
<media type="image" mime="image/png" src="figures/virtual-cnc-3.png"/>
</figure>
+ </section>
+ <section id="virtual-ddl-sql">
+ <title>SQL useable with virtual connections</title>
+ <p>
+ You can use the SQL understood by <link href="http://sqlite.org/lang.html">SQLite</link> in any virtual
+ connection. Be aware however that if you define database objects (outside of the extended SQL
+ presented next section), they will be lost the next time the virtual connection is opened.
+ </p>
+ <p>
+ See the <link xref="sql-sqlite">SQL understood by LDAP connections and virtual connections</link>
+ section for more information.
+ </p>
</section>
</page>
diff --git a/tools/browser/help/Makefile.am b/tools/browser/help/Makefile.am
index 3bba571..93acaf8 100644
--- a/tools/browser/help/Makefile.am
+++ b/tools/browser/help/Makefile.am
@@ -27,7 +27,11 @@ DOC_FIGURES = \
figures/virtual-cnc-2.png \
figures/virtual-cnc-3.png \
figures/virtual-cnc-4.png \
- figures/virtual-cnc-5.png
+ figures/virtual-cnc-5.png \
+ figures/ldap-browser-persp.png \
+ figures/ldap-classes.png \
+ figures/ldap-search.png \
+ figures/ldap-table-mapping.png
DOC_PAGES = \
actions.page \
@@ -45,7 +49,9 @@ DOC_PAGES = \
transactions.page \
table-insert-data.page \
variables.page \
- virtual-connections.page
+ virtual-connections.page \
+ ldap-browser-perspective.page \
+ ldap-connections.page
DOC_LINGUAS = de es sl zh_CN
diff --git a/tools/browser/ldap-browser/Makefile.am b/tools/browser/ldap-browser/Makefile.am
new file mode 100644
index 0000000..92c6f1b
--- /dev/null
+++ b/tools/browser/ldap-browser/Makefile.am
@@ -0,0 +1,58 @@
+noinst_LTLIBRARIES = libperspective.la
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir)/tools/browser \
+ -I$(top_builddir) \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/libgda \
+ -I$(top_srcdir)/libgda/sqlite \
+ $(COREDEPS_CFLAGS) \
+ $(COREDEPS_WFLAGS) \
+ $(GTK_CFLAGS) -DHAVE_LDAP \
+ $(MAC_INTEGRATION_CFLAGS)
+
+marshal.h: marshal.list $(GLIB_GENMARSHAL)
+ $(GLIB_GENMARSHAL) $< --header --prefix=_ldap_marshal > $@
+marshal.c: marshal.list $(GLIB_GENMARSHAL) marshal.h
+ $(GLIB_GENMARSHAL) $< --body --prefix=_ldap_marshal > $@
+
+libperspective_la_SOURCES = \
+ marshal.c \
+ marshal.h \
+ perspective-main.h \
+ perspective-main.c \
+ ldap-browser-perspective.h \
+ ldap-browser-perspective.c \
+ ldap-favorite-selector.h \
+ ldap-favorite-selector.c \
+ ldap-entries-page.c \
+ ldap-entries-page.h \
+ mgr-ldap-entries.h \
+ mgr-ldap-entries.c \
+ hierarchy-view.h \
+ hierarchy-view.c \
+ entry-properties.h \
+ entry-properties.c \
+ mgr-ldap-classes.h \
+ mgr-ldap-classes.c \
+ classes-view.h \
+ classes-view.c \
+ ldap-classes-page.h \
+ ldap-classes-page.c \
+ class-properties.h \
+ class-properties.c \
+ filter-editor.h \
+ filter-editor.c \
+ ldap-search-page.h \
+ ldap-search-page.c \
+ vtable-dialog.h \
+ vtable-dialog.c
+
+$(OBJECTS): marshal.c marshal.h
+
+EXTRA_DIST = \
+ marshal.list
+
+CLEANFILES = \
+ marshal.h \
+ marshal.c
diff --git a/tools/browser/ldap-browser/class-properties.c b/tools/browser/ldap-browser/class-properties.c
new file mode 100644
index 0000000..2240e2a
--- /dev/null
+++ b/tools/browser/ldap-browser/class-properties.c
@@ -0,0 +1,576 @@
+/*
+ * Copyright (C) 2011 The GNOME Foundation
+ *
+ * AUTHORS:
+ * Vivien Malerba <malerba gnome-db org>
+ *
+ * This Program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <glib/gi18n-lib.h>
+#include <string.h>
+#include <gtk/gtk.h>
+#include "class-properties.h"
+#include "marshal.h"
+
+struct _ClassPropertiesPrivate {
+ BrowserConnection *bcnc;
+
+ GtkTextBuffer *text;
+ gboolean hovering_over_link;
+};
+
+static void class_properties_class_init (ClassPropertiesClass *klass);
+static void class_properties_init (ClassProperties *eprop, ClassPropertiesClass *klass);
+static void class_properties_dispose (GObject *object);
+
+static GObjectClass *parent_class = NULL;
+
+/* signals */
+enum {
+ OPEN_CLASS,
+ LAST_SIGNAL
+};
+
+gint class_properties_signals [LAST_SIGNAL] = { 0 };
+
+/*
+ * ClassProperties class implementation
+ */
+
+static void
+class_properties_class_init (ClassPropertiesClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ parent_class = g_type_class_peek_parent (klass);
+
+ class_properties_signals [OPEN_CLASS] =
+ g_signal_new ("open-class",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (ClassPropertiesClass, open_class),
+ NULL, NULL,
+ _ldap_marshal_VOID__STRING, G_TYPE_NONE,
+ 1, G_TYPE_STRING);
+ klass->open_class = NULL;
+
+ object_class->dispose = class_properties_dispose;
+}
+
+
+static void
+class_properties_init (ClassProperties *eprop, G_GNUC_UNUSED ClassPropertiesClass *klass)
+{
+ eprop->priv = g_new0 (ClassPropertiesPrivate, 1);
+ eprop->priv->hovering_over_link = FALSE;
+}
+
+static void
+class_properties_dispose (GObject *object)
+{
+ ClassProperties *eprop = (ClassProperties *) object;
+
+ /* free memory */
+ if (eprop->priv) {
+ if (eprop->priv->bcnc) {
+ g_object_unref (eprop->priv->bcnc);
+ }
+ g_free (eprop->priv);
+ eprop->priv = NULL;
+ }
+
+ parent_class->dispose (object);
+}
+
+GType
+class_properties_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0)) {
+ static const GTypeInfo columns = {
+ sizeof (ClassPropertiesClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) class_properties_class_init,
+ NULL,
+ NULL,
+ sizeof (ClassProperties),
+ 0,
+ (GInstanceInitFunc) class_properties_init,
+ 0
+ };
+ type = g_type_register_static (GTK_TYPE_VBOX, "ClassProperties", &columns, 0);
+ }
+ return type;
+}
+
+static gboolean key_press_event (GtkWidget *text_view, GdkEventKey *event, ClassProperties *eprop);
+static gboolean event_after (GtkWidget *text_view, GdkEvent *ev, ClassProperties *eprop);
+static gboolean motion_notify_event (GtkWidget *text_view, GdkEventMotion *event, ClassProperties *eprop);
+static gboolean visibility_notify_event (GtkWidget *text_view, GdkEventVisibility *event, ClassProperties *eprop);
+
+/**
+ * class_properties_new:
+ *
+ * Returns: a new #GtkWidget
+ */
+GtkWidget *
+class_properties_new (BrowserConnection *bcnc)
+{
+ ClassProperties *eprop;
+ g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), NULL);
+
+ eprop = CLASS_PROPERTIES (g_object_new (CLASS_PROPERTIES_TYPE, NULL));
+ eprop->priv->bcnc = g_object_ref (bcnc);
+
+ GtkWidget *sw;
+ sw = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw), GTK_SHADOW_NONE);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+ gtk_box_pack_start (GTK_BOX (eprop), sw, TRUE, TRUE, 0);
+
+ GtkWidget *textview;
+ textview = gtk_text_view_new ();
+ gtk_container_add (GTK_CONTAINER (sw), textview);
+ gtk_text_view_set_left_margin (GTK_TEXT_VIEW (textview), 5);
+ gtk_text_view_set_right_margin (GTK_TEXT_VIEW (textview), 5);
+ gtk_text_view_set_editable (GTK_TEXT_VIEW (textview), FALSE);
+ gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (textview), FALSE);
+ eprop->priv->text = gtk_text_view_get_buffer (GTK_TEXT_VIEW (textview));
+ gtk_widget_show_all (sw);
+
+ gtk_text_buffer_create_tag (eprop->priv->text, "section",
+ "weight", PANGO_WEIGHT_BOLD,
+ "foreground", "blue", NULL);
+
+ gtk_text_buffer_create_tag (eprop->priv->text, "error",
+ "foreground", "red", NULL);
+
+ gtk_text_buffer_create_tag (eprop->priv->text, "data",
+ "left-margin", 20, NULL);
+
+ gtk_text_buffer_create_tag (eprop->priv->text, "starter",
+ "indent", -10,
+ "left-margin", 20, NULL);
+
+ g_signal_connect (textview, "key-press-event",
+ G_CALLBACK (key_press_event), eprop);
+ g_signal_connect (textview, "event-after",
+ G_CALLBACK (event_after), eprop);
+ g_signal_connect (textview, "motion-notify-event",
+ G_CALLBACK (motion_notify_event), eprop);
+ g_signal_connect (textview, "visibility-notify-event",
+ G_CALLBACK (visibility_notify_event), eprop);
+
+ class_properties_set_class (eprop, NULL);
+
+ return (GtkWidget*) eprop;
+}
+
+static GdkCursor *hand_cursor = NULL;
+static GdkCursor *regular_cursor = NULL;
+
+/* Looks at all tags covering the position (x, y) in the text view,
+ * and if one of them is a link, change the cursor to the "hands" cursor
+ * typically used by web browsers.
+ */
+static void
+set_cursor_if_appropriate (GtkTextView *text_view, gint x, gint y, ClassProperties *eprop)
+{
+ GSList *tags = NULL, *tagp = NULL;
+ GtkTextIter iter;
+ gboolean hovering = FALSE;
+
+ gtk_text_view_get_iter_at_location (text_view, &iter, x, y);
+
+ tags = gtk_text_iter_get_tags (&iter);
+ for (tagp = tags; tagp != NULL; tagp = tagp->next) {
+ GtkTextTag *tag = tagp->data;
+
+ if (g_object_get_data (G_OBJECT (tag), "class")) {
+ hovering = TRUE;
+ break;
+ }
+ }
+
+ if (hovering != eprop->priv->hovering_over_link) {
+ eprop->priv->hovering_over_link = hovering;
+
+ if (eprop->priv->hovering_over_link) {
+ if (! hand_cursor)
+ hand_cursor = gdk_cursor_new (GDK_HAND2);
+ gdk_window_set_cursor (gtk_text_view_get_window (text_view,
+ GTK_TEXT_WINDOW_TEXT),
+ hand_cursor);
+ }
+ else {
+ if (!regular_cursor)
+ regular_cursor = gdk_cursor_new (GDK_XTERM);
+ gdk_window_set_cursor (gtk_text_view_get_window (text_view,
+ GTK_TEXT_WINDOW_TEXT),
+ regular_cursor);
+ }
+ }
+
+ if (tags)
+ g_slist_free (tags);
+}
+
+/*
+ * Also update the cursor image if the window becomes visible
+ * (e.g. when a window covering it got iconified).
+ */
+static gboolean
+visibility_notify_event (GtkWidget *text_view, G_GNUC_UNUSED GdkEventVisibility *event,
+ ClassProperties *eprop)
+{
+ gint wx, wy, bx, by;
+
+ gdk_window_get_pointer (gtk_widget_get_window (text_view), &wx, &wy, NULL);
+
+ gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (text_view),
+ GTK_TEXT_WINDOW_WIDGET,
+ wx, wy, &bx, &by);
+
+ set_cursor_if_appropriate (GTK_TEXT_VIEW (text_view), bx, by, eprop);
+
+ return FALSE;
+}
+
+/*
+ * Update the cursor image if the pointer moved.
+ */
+static gboolean
+motion_notify_event (GtkWidget *text_view, GdkEventMotion *event, ClassProperties *eprop)
+{
+ gint x, y;
+
+ gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (text_view),
+ GTK_TEXT_WINDOW_WIDGET,
+ event->x, event->y, &x, &y);
+
+ set_cursor_if_appropriate (GTK_TEXT_VIEW (text_view), x, y, eprop);
+
+ gdk_window_get_pointer (gtk_widget_get_window (text_view), NULL, NULL, NULL);
+
+ return FALSE;
+}
+
+/* Looks at all tags covering the position of iter in the text view,
+ * and if one of them is a link, follow it by showing the page identified
+ * by the data attached to it.
+ */
+static void
+follow_if_link (G_GNUC_UNUSED GtkWidget *text_view, GtkTextIter *iter, ClassProperties *eprop)
+{
+ GSList *tags = NULL, *tagp = NULL;
+
+ tags = gtk_text_iter_get_tags (iter);
+ for (tagp = tags; tagp != NULL; tagp = tagp->next) {
+ GtkTextTag *tag = tagp->data;
+ const gchar *dn;
+
+ dn = g_object_get_data (G_OBJECT (tag), "class");
+ if (dn)
+ g_signal_emit (eprop, class_properties_signals [OPEN_CLASS], 0, dn);
+ }
+
+ if (tags)
+ g_slist_free (tags);
+}
+
+
+/*
+ * Links can also be activated by clicking.
+ */
+static gboolean
+event_after (GtkWidget *text_view, GdkEvent *ev, ClassProperties *eprop)
+{
+ GtkTextIter start, end, iter;
+ GtkTextBuffer *buffer;
+ GdkEventButton *event;
+ gint x, y;
+
+ if (ev->type != GDK_BUTTON_RELEASE)
+ return FALSE;
+
+ event = (GdkEventButton *)ev;
+
+ if (event->button != 1)
+ return FALSE;
+
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view));
+
+ /* we shouldn't follow a link if the user has selected something */
+ gtk_text_buffer_get_selection_bounds (buffer, &start, &end);
+ if (gtk_text_iter_get_offset (&start) != gtk_text_iter_get_offset (&end))
+ return FALSE;
+
+ gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (text_view),
+ GTK_TEXT_WINDOW_WIDGET,
+ event->x, event->y, &x, &y);
+
+ gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW (text_view), &iter, x, y);
+
+ follow_if_link (text_view, &iter, eprop);
+
+ return FALSE;
+}
+
+/*
+ * Links can be activated by pressing Enter.
+ */
+static gboolean
+key_press_event (GtkWidget *text_view, GdkEventKey *event, ClassProperties *eprop)
+{
+ GtkTextIter iter;
+ GtkTextBuffer *buffer;
+
+ switch (event->keyval) {
+ case GDK_KEY_Return:
+ case GDK_KEY_KP_Enter:
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view));
+ gtk_text_buffer_get_iter_at_mark (buffer, &iter,
+ gtk_text_buffer_get_insert (buffer));
+ follow_if_link (text_view, &iter, eprop);
+ break;
+
+ default:
+ break;
+ }
+ return FALSE;
+}
+
+/**
+ * class_properties_set_class:
+ * @eprop: a #ClassProperties widget
+ * @classname: a DN to display information for
+ *
+ * Adjusts the display to show @classname's properties
+ */
+void
+class_properties_set_class (ClassProperties *eprop, const gchar *classname)
+{
+ g_return_if_fail (IS_CLASS_PROPERTIES (eprop));
+
+ GtkTextBuffer *tbuffer;
+ GtkTextIter start, end;
+ GdaLdapClass *lcl;
+ GtkTextIter current;
+ gint i;
+
+ tbuffer = eprop->priv->text;
+ gtk_text_buffer_get_start_iter (tbuffer, &start);
+ gtk_text_buffer_get_end_iter (tbuffer, &end);
+ gtk_text_buffer_delete (tbuffer, &start, &end);
+
+ if (!classname || !*classname)
+ return;
+
+ lcl = browser_connection_get_class_info (eprop->priv->bcnc, classname);
+ if (!lcl) {
+ browser_show_message (GTK_WINDOW (gtk_widget_get_toplevel ((GtkWidget*) eprop)),
+ "%s", _("Could not get information about LDAP class"));
+ return;
+ }
+
+ gtk_text_buffer_get_start_iter (tbuffer, ¤t);
+
+ /* Description */
+ if (lcl->description) {
+ gtk_text_buffer_insert_with_tags_by_name (tbuffer, ¤t,
+ _("Description:"), -1,
+ "section", NULL);
+ gtk_text_buffer_insert (tbuffer, ¤t, "\n", -1);
+ gtk_text_buffer_insert_with_tags_by_name (tbuffer, ¤t, " ", -1, "starter", NULL);
+ gtk_text_buffer_insert_with_tags_by_name (tbuffer, ¤t, lcl->description, -1,
+ "data", NULL);
+ gtk_text_buffer_insert (tbuffer, ¤t, "\n", -1);
+ }
+
+ /* OID */
+ gtk_text_buffer_insert_with_tags_by_name (tbuffer, ¤t,
+ _("Class OID:"), -1,
+ "section", NULL);
+ gtk_text_buffer_insert (tbuffer, ¤t, "\n", -1);
+ gtk_text_buffer_insert_with_tags_by_name (tbuffer, ¤t, " ", -1, "starter", NULL);
+ gtk_text_buffer_insert_with_tags_by_name (tbuffer, ¤t, lcl->oid, -1,
+ "data", NULL);
+ gtk_text_buffer_insert (tbuffer, ¤t, "\n", -1);
+
+ /* Kind */
+ const gchar *kind;
+ gtk_text_buffer_insert_with_tags_by_name (tbuffer, ¤t,
+ _("Class kind:"), -1,
+ "section", NULL);
+ gtk_text_buffer_insert (tbuffer, ¤t, "\n", 1);
+
+ gtk_text_buffer_insert_pixbuf (tbuffer, ¤t, browser_get_pixbuf_for_ldap_class (lcl->kind));
+
+ kind = browser_get_kind_for_ldap_class (lcl->kind);
+ gtk_text_buffer_insert_with_tags_by_name (tbuffer, ¤t, " ", -1, "starter", NULL);
+ gtk_text_buffer_insert_with_tags_by_name (tbuffer, ¤t, kind, -1,
+ "data", NULL);
+ gtk_text_buffer_insert (tbuffer, ¤t, "\n", -1);
+
+ /* Class name */
+ gtk_text_buffer_insert_with_tags_by_name (tbuffer, ¤t,
+ ngettext ("Class name:", "Class names:", lcl->nb_names), -1,
+ "section", NULL);
+ gtk_text_buffer_insert (tbuffer, ¤t, "\n", -1);
+ for (i = 0; i < lcl->nb_names; i++) {
+ gtk_text_buffer_insert_with_tags_by_name (tbuffer, ¤t, " ", -1, "starter", NULL);
+ gtk_text_buffer_insert_with_tags_by_name (tbuffer, ¤t, lcl->names[i], -1,
+ "data", NULL);
+ gtk_text_buffer_insert (tbuffer, ¤t, "\n", -1);
+ }
+
+ /* obsolete */
+ if (lcl->obsolete) {
+ gtk_text_buffer_insert_with_tags_by_name (tbuffer, ¤t,
+ _("This LDAP class is obsolete"), -1,
+ "error", NULL);
+ gtk_text_buffer_insert (tbuffer, ¤t, "\n", -1);
+ }
+
+ /* req. attributes */
+ if (lcl->nb_req_attributes > 0) {
+ gtk_text_buffer_insert_with_tags_by_name (tbuffer, ¤t,
+ ngettext ("Required attribute:",
+ "Required attributes:",
+ lcl->nb_req_attributes), -1,
+ "section", NULL);
+ gtk_text_buffer_insert (tbuffer, ¤t, "\n", -1);
+ for (i = 0; i < lcl->nb_req_attributes; i++) {
+ gtk_text_buffer_insert_with_tags_by_name (tbuffer, ¤t, " ", -1, "starter", NULL);
+ gtk_text_buffer_insert_with_tags_by_name (tbuffer, ¤t, lcl->req_attributes[i], -1,
+ "data", NULL);
+ gtk_text_buffer_insert (tbuffer, ¤t, "\n", -1);
+ }
+ }
+
+ /* opt. attributes */
+ if (lcl->nb_opt_attributes > 0) {
+ gtk_text_buffer_insert_with_tags_by_name (tbuffer, ¤t,
+ ngettext ("Optional attribute:",
+ "Optional attributes:",
+ lcl->nb_opt_attributes), -1,
+ "section", NULL);
+ gtk_text_buffer_insert (tbuffer, ¤t, "\n", -1);
+ for (i = 0; i < lcl->nb_opt_attributes; i++) {
+ gtk_text_buffer_insert_with_tags_by_name (tbuffer, ¤t, " ", -1, "starter", NULL);
+ gtk_text_buffer_insert_with_tags_by_name (tbuffer, ¤t, lcl->opt_attributes[i], -1,
+ "data", NULL);
+ gtk_text_buffer_insert (tbuffer, ¤t, "\n", -1);
+ }
+ }
+
+ /* children */
+ if (lcl->children) {
+ gint nb;
+ GSList *list;
+ nb = g_slist_length (lcl->children);
+ gtk_text_buffer_insert_with_tags_by_name (tbuffer, ¤t,
+ ngettext ("Children class:",
+ "Children classes:",
+ nb), -1,
+ "section", NULL);
+ gtk_text_buffer_insert (tbuffer, ¤t, "\n", -1);
+ for (list = lcl->children; list; list = list->next) {
+ GdaLdapClass *olcl;
+ gchar *tmp;
+ GtkTextTag *tag;
+ olcl = (GdaLdapClass*) list->data;
+ gtk_text_buffer_insert_pixbuf (tbuffer, ¤t,
+ browser_get_pixbuf_for_ldap_class (olcl->kind));
+ gtk_text_buffer_insert_with_tags_by_name (tbuffer, ¤t, " ", -1, "starter", NULL);
+ tag = gtk_text_buffer_create_tag (tbuffer, NULL,
+ "foreground", "blue",
+ "weight", PANGO_WEIGHT_NORMAL,
+ "underline", PANGO_UNDERLINE_SINGLE,
+ NULL);
+ tmp = olcl->names [0];
+ g_object_set_data_full (G_OBJECT (tag), "class",
+ g_strdup (tmp), g_free);
+ gtk_text_buffer_insert_with_tags (tbuffer, ¤t,
+ tmp, -1,
+ tag, NULL);
+ if (olcl->description) {
+ gtk_text_buffer_insert_with_tags_by_name (tbuffer, ¤t,
+ " (", -1,
+ "data", NULL);
+ gtk_text_buffer_insert_with_tags_by_name (tbuffer, ¤t,
+ olcl->description, -1,
+ "data", NULL);
+ gtk_text_buffer_insert_with_tags_by_name (tbuffer, ¤t,
+ ")", -1,
+ "data", NULL);
+ }
+ gtk_text_buffer_insert (tbuffer, ¤t, "\n", -1);
+ }
+ }
+
+ /* parents */
+ if (lcl->parents) {
+ gint nb;
+ GSList *list;
+ nb = g_slist_length (lcl->parents);
+ gtk_text_buffer_insert_with_tags_by_name (tbuffer, ¤t,
+ ngettext ("Inherited class:",
+ "Inherited classes:",
+ nb), -1,
+ "section", NULL);
+ gtk_text_buffer_insert (tbuffer, ¤t, "\n", -1);
+ for (list = lcl->parents; list; list = list->next) {
+ GdaLdapClass *olcl;
+ gchar *tmp;
+ GtkTextTag *tag;
+ olcl = (GdaLdapClass*) list->data;
+
+ gtk_text_buffer_insert_pixbuf (tbuffer, ¤t,
+ browser_get_pixbuf_for_ldap_class (olcl->kind));
+ gtk_text_buffer_insert_with_tags_by_name (tbuffer, ¤t, " ", -1, "starter", NULL);
+ tag = gtk_text_buffer_create_tag (tbuffer, NULL,
+ "foreground", "blue",
+ "weight", PANGO_WEIGHT_NORMAL,
+ "underline", PANGO_UNDERLINE_SINGLE,
+ NULL);
+ tmp = olcl->names [0];
+ g_object_set_data_full (G_OBJECT (tag), "class",
+ g_strdup (tmp), g_free);
+ gtk_text_buffer_insert_with_tags (tbuffer, ¤t,
+ tmp, -1,
+ tag, NULL);
+
+ if (olcl->description) {
+ gtk_text_buffer_insert_with_tags_by_name (tbuffer, ¤t,
+ " (", -1,
+ "data", NULL);
+ gtk_text_buffer_insert_with_tags_by_name (tbuffer, ¤t,
+ olcl->description, -1,
+ "data", NULL);
+ gtk_text_buffer_insert_with_tags_by_name (tbuffer, ¤t,
+ ")", -1,
+ "data", NULL);
+ }
+
+ gtk_text_buffer_insert (tbuffer, ¤t, "\n", -1);
+ }
+ }
+}
diff --git a/tools/browser/ldap-browser/class-properties.h b/tools/browser/ldap-browser/class-properties.h
new file mode 100644
index 0000000..ddb5e08
--- /dev/null
+++ b/tools/browser/ldap-browser/class-properties.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2011 The GNOME Foundation
+ *
+ * AUTHORS:
+ * Vivien Malerba <malerba gnome-db org>
+ *
+ * This Program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __CLASS_PROPERTIES_H__
+#define __CLASS_PROPERTIES_H__
+
+#include "../browser-connection.h"
+
+G_BEGIN_DECLS
+
+#define CLASS_PROPERTIES_TYPE (class_properties_get_type())
+#define CLASS_PROPERTIES(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, CLASS_PROPERTIES_TYPE, ClassProperties))
+#define CLASS_PROPERTIES_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, CLASS_PROPERTIES_TYPE, ClassPropertiesClass))
+#define IS_CLASS_PROPERTIES(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, CLASS_PROPERTIES_TYPE))
+#define IS_CLASS_PROPERTIES_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLASS_PROPERTIES_TYPE))
+
+typedef struct _ClassProperties ClassProperties;
+typedef struct _ClassPropertiesClass ClassPropertiesClass;
+typedef struct _ClassPropertiesPrivate ClassPropertiesPrivate;
+
+struct _ClassProperties {
+ GtkVBox parent;
+ ClassPropertiesPrivate *priv;
+};
+
+struct _ClassPropertiesClass {
+ GtkVBoxClass parent_class;
+
+ /* signals */
+ void (*open_class) (ClassProperties *eprop, const gchar *dn);
+};
+
+GType class_properties_get_type (void) G_GNUC_CONST;
+
+GtkWidget *class_properties_new (BrowserConnection *bcnc);
+void class_properties_set_class (ClassProperties *eprop, const gchar *classname);
+
+G_END_DECLS
+
+#endif
diff --git a/tools/browser/ldap-browser/classes-view.c b/tools/browser/ldap-browser/classes-view.c
new file mode 100644
index 0000000..b9087db
--- /dev/null
+++ b/tools/browser/ldap-browser/classes-view.c
@@ -0,0 +1,408 @@
+/*
+ * Copyright (C) 2011 The GNOME Foundation
+ *
+ * AUTHORS:
+ * Vivien Malerba <malerba gnome-db org>
+ *
+ * This Program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <glib/gi18n-lib.h>
+#include <string.h>
+#include "classes-view.h"
+#include "../dnd.h"
+#include "../support.h"
+#include "../cc-gray-bar.h"
+#include "../browser-stock-icons.h"
+#include <virtual/gda-ldap-connection.h>
+#include "mgr-ldap-classes.h"
+#include <libgda-ui/gdaui-tree-store.h>
+
+struct _ClassesViewPrivate {
+ BrowserConnection *bcnc;
+
+ GdaTree *classes_tree;
+ GdauiTreeStore *classes_store;
+
+ gchar *current_class;
+};
+
+static void classes_view_class_init (ClassesViewClass *klass);
+static void classes_view_init (ClassesView *eview, ClassesViewClass *klass);
+static void classes_view_dispose (GObject *object);
+static void classes_view_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void classes_view_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+/* properties */
+enum {
+ PROP_0,
+};
+
+static GObjectClass *parent_class = NULL;
+
+
+/*
+ * ClassesView class implementation
+ */
+
+static void
+classes_view_class_init (ClassesViewClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ /* Properties */
+ object_class->set_property = classes_view_set_property;
+ object_class->get_property = classes_view_get_property;
+
+ object_class->dispose = classes_view_dispose;
+}
+
+static void
+classes_view_init (ClassesView *eview, G_GNUC_UNUSED ClassesViewClass *klass)
+{
+ eview->priv = g_new0 (ClassesViewPrivate, 1);
+ eview->priv->current_class = NULL;
+}
+
+static void
+classes_view_dispose (GObject *object)
+{
+ ClassesView *eview = (ClassesView *) object;
+
+ /* free memory */
+ if (eview->priv) {
+ if (eview->priv->bcnc)
+ g_object_unref (eview->priv->bcnc);
+ if (eview->priv->classes_tree)
+ g_object_unref (eview->priv->classes_tree);
+
+ g_free (eview->priv);
+ eview->priv = NULL;
+ }
+
+ parent_class->dispose (object);
+}
+
+GType
+classes_view_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0)) {
+ static const GTypeInfo info = {
+ sizeof (ClassesViewClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) classes_view_class_init,
+ NULL,
+ NULL,
+ sizeof (ClassesView),
+ 0,
+ (GInstanceInitFunc) classes_view_init,
+ 0
+ };
+
+ type = g_type_register_static (GTK_TYPE_TREE_VIEW, "ClassesView", &info, 0);
+ }
+ return type;
+}
+
+static void
+classes_view_set_property (GObject *object,
+ guint param_id,
+ G_GNUC_UNUSED const GValue *value,
+ GParamSpec *pspec)
+{
+ ClassesView *eview;
+ eview = CLASSES_VIEW (object);
+ switch (param_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+static void
+classes_view_get_property (GObject *object,
+ guint param_id,
+ G_GNUC_UNUSED GValue *value,
+ GParamSpec *pspec)
+{
+ ClassesView *eview;
+ eview = CLASSES_VIEW (object);
+ switch (param_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+static gchar *
+classes_view_to_selection (G_GNUC_UNUSED ClassesView *eview)
+{
+ /*
+ GString *string;
+ gchar *tmp;
+ string = g_string_new ("OBJ_TYPE=classes");
+ tmp = gda_rfc1738_encode (eview->priv->schema);
+ g_string_append_printf (string, ";OBJ_SCHEMA=%s", tmp);
+ g_free (tmp);
+ tmp = gda_rfc1738_encode (eview->priv->classes_name);
+ g_string_append_printf (string, ";OBJ_NAME=%s", tmp);
+ g_free (tmp);
+ tmp = gda_rfc1738_encode (eview->priv->classes_short_name);
+ g_string_append_printf (string, ";OBJ_SHORT_NAME=%s", tmp);
+ g_free (tmp);
+ return g_string_free (string, FALSE);
+ */
+ if (eview->priv->current_class)
+ return g_strdup (eview->priv->current_class);
+ else
+ return NULL;
+}
+
+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)
+{
+ switch (view) {
+ case TARGET_KEY_VALUE: {
+ gchar *str;
+ str = classes_view_to_selection (eview);
+ gtk_selection_data_set (selection_data,
+ gtk_selection_data_get_target (selection_data), 8, (guchar*) str,
+ strlen (str));
+ g_free (str);
+ break;
+ }
+ default:
+ case TARGET_PLAIN: {
+ gtk_selection_data_set_text (selection_data, classes_view_get_current_class (eview), -1);
+ break;
+ }
+ case TARGET_ROOTWIN:
+ TO_IMPLEMENT; /* dropping on the Root Window => create a file */
+ break;
+ }
+}
+
+static void selection_changed_cb (GtkTreeSelection *sel, ClassesView *eview);
+
+enum {
+ COLUMN_CLASS,
+ COLUMN_ICON,
+ COLUMN_NAME,
+ NUM_COLUMNS
+};
+
+static void
+text_cell_data_func (GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
+ GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
+{
+ gchar *tmp;
+ gtk_tree_model_get (tree_model, iter,
+ COLUMN_CLASS, &tmp, -1);
+ if (tmp) {
+ g_object_set ((GObject*) cell, "text", tmp,
+ "weight-set", FALSE,
+ "background-set", FALSE,
+ NULL);
+ g_free (tmp);
+ }
+ else {
+ gtk_tree_model_get (tree_model, iter,
+ COLUMN_NAME, &tmp, -1);
+ g_object_set ((GObject*) cell, "text", tmp,
+ "weight", PANGO_WEIGHT_BOLD,
+ "background", "grey", NULL);
+ g_free (tmp);
+ }
+}
+
+/**
+ * classes_view_new
+ *
+ * Returns: a new #GtkWidget
+ */
+GtkWidget *
+classes_view_new (BrowserConnection *bcnc, const gchar *classname)
+{
+ ClassesView *eview;
+
+ g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), NULL);
+
+ eview = CLASSES_VIEW (g_object_new (CLASSES_VIEW_TYPE, NULL));
+ eview->priv->bcnc = g_object_ref ((GObject*) bcnc);
+ g_signal_connect (eview, "drag-data-get",
+ G_CALLBACK (source_drag_data_get_cb), eview);
+
+ GdaTreeManager *mgr;
+ GtkTreeModel *store;
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+ eview->priv->classes_tree = gda_tree_new ();
+ mgr = mgr_ldap_classes_new (eview->priv->bcnc, FALSE, NULL);
+ gda_tree_add_manager (eview->priv->classes_tree, mgr);
+ gda_tree_manager_add_manager (mgr, mgr);
+ gda_tree_update_all (eview->priv->classes_tree, NULL);
+ g_object_unref (mgr);
+
+ store = gdaui_tree_store_new (eview->priv->classes_tree, NUM_COLUMNS,
+ G_TYPE_STRING, "class",
+ G_TYPE_OBJECT, "icon",
+ G_TYPE_STRING, GDA_ATTRIBUTE_NAME);
+ gtk_tree_view_set_model (GTK_TREE_VIEW (eview), GTK_TREE_MODEL (store));
+
+ eview->priv->classes_store = GDAUI_TREE_STORE (store);
+ g_object_unref (G_OBJECT (store));
+
+ column = gtk_tree_view_column_new ();
+
+ renderer = gtk_cell_renderer_pixbuf_new ();
+ gtk_tree_view_column_pack_start (column, renderer, FALSE);
+ gtk_tree_view_column_add_attribute (column, renderer, "pixbuf", COLUMN_ICON);
+ g_object_set ((GObject*) renderer, "yalign", 0., NULL);
+
+ renderer = gtk_cell_renderer_text_new ();
+ gtk_tree_view_column_pack_start (column, renderer, TRUE);
+ gtk_tree_view_column_set_cell_data_func (column, renderer,
+ (GtkTreeCellDataFunc) text_cell_data_func,
+ NULL, NULL);
+
+ gtk_tree_view_append_column (GTK_TREE_VIEW (eview), column);
+ gtk_tree_view_set_expander_column (GTK_TREE_VIEW (eview), column);
+ gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (eview), FALSE);
+
+ /* tree selection */
+ GtkTreeSelection *sel;
+ sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (eview));
+ gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
+ g_signal_connect (sel, "changed",
+ G_CALLBACK (selection_changed_cb), eview);
+
+ if (classname)
+ classes_view_set_current_class (eview, classname);
+
+ return (GtkWidget*) eview;
+}
+
+static void
+selection_changed_cb (GtkTreeSelection *sel, ClassesView *eview)
+{
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+ if (gtk_tree_selection_get_selected (sel, &model, &iter)) {
+ GdaTreeNode *node;
+ const GValue *cvalue;
+ node = gdaui_tree_store_get_node (GDAUI_TREE_STORE (model), &iter);
+ g_assert (node);
+ cvalue = gda_tree_node_get_node_attribute (node, "class");
+ g_free (eview->priv->current_class);
+ if (cvalue)
+ eview->priv->current_class = g_value_dup_string (cvalue);
+ else
+ eview->priv->current_class = NULL;
+ }
+}
+
+/**
+ * classes_view_get_current_class:
+ */
+const gchar *
+classes_view_get_current_class (ClassesView *eview)
+{
+ g_return_val_if_fail (IS_CLASSES_VIEW (eview), NULL);
+ return eview->priv->current_class;
+}
+
+static GtkTreePath *
+search_for_class (GtkTreeModel *model, const gchar *classname, GtkTreeIter *iter)
+{
+#ifdef GDA_DEBUG_NO
+ GtkTreePath *debug;
+ if (iter) {
+ debug = gtk_tree_model_get_path (model, iter);
+ g_print ("%s (%s)\n", __FUNCTION__, gtk_tree_path_to_string (debug));
+ gtk_tree_path_free (debug);
+ }
+ else
+ g_print ("%s (TOP)\n", __FUNCTION__);
+#endif
+
+ if (iter) {
+ /* look in the node itself */
+ gchar *cln = NULL;
+ gtk_tree_model_get (model, iter, COLUMN_CLASS, &cln, -1);
+ if (cln && !strcmp (cln, classname)) {
+ g_free (cln);
+ return gtk_tree_model_get_path (model, iter);
+ }
+ g_free (cln);
+ }
+
+ /* look at the children */
+ GtkTreeIter chiter;
+ if (gtk_tree_model_iter_children (model, &chiter, iter)) {
+ GtkTreePath *path;
+ path = search_for_class (model, classname, &chiter);
+ if (path)
+ return path;
+
+ for (; gtk_tree_model_iter_next (model, &chiter); ) {
+ GtkTreePath *path;
+ path = search_for_class (model, classname, &chiter);
+ if (path)
+ return path;
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * classes_view_set_current_class:
+ */
+void
+classes_view_set_current_class (ClassesView *eview, const gchar *classname)
+{
+ GtkTreePath *path = NULL;
+ g_return_if_fail (IS_CLASSES_VIEW (eview));
+ g_return_if_fail (classname && *classname);
+
+ path = search_for_class (GTK_TREE_MODEL (eview->priv->classes_store), classname, NULL);
+ if (path) {
+ GtkTreeSelection *sel;
+
+#ifdef GDA_DEBUG_NO
+ g_print ("Found class [%s] at %s\n", classname, gtk_tree_path_to_string (path));
+#endif
+ gtk_tree_view_expand_to_path (GTK_TREE_VIEW (eview), path);
+ gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (eview), path, NULL, TRUE, .5, 0.);
+
+ sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (eview));
+ gtk_tree_selection_select_path (sel, path);
+ gtk_tree_path_free (path);
+ }
+}
diff --git a/tools/browser/ldap-browser/classes-view.h b/tools/browser/ldap-browser/classes-view.h
new file mode 100644
index 0000000..7139979
--- /dev/null
+++ b/tools/browser/ldap-browser/classes-view.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2011 The GNOME Foundation
+ *
+ * AUTHORS:
+ * Vivien Malerba <malerba gnome-db org>
+ *
+ * This Program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __CLASSES_VIEW_H__
+#define __CLASSES_VIEW_H__
+
+#include <gtk/gtk.h>
+#include "../browser-connection.h"
+
+G_BEGIN_DECLS
+
+#define CLASSES_VIEW_TYPE (classes_view_get_type())
+#define CLASSES_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, CLASSES_VIEW_TYPE, ClassesView))
+#define CLASSES_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, CLASSES_VIEW_TYPE, ClassesViewClass))
+#define IS_CLASSES_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, CLASSES_VIEW_TYPE))
+#define IS_CLASSES_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLASSES_VIEW_TYPE))
+
+typedef struct _ClassesView ClassesView;
+typedef struct _ClassesViewClass ClassesViewClass;
+typedef struct _ClassesViewPrivate ClassesViewPrivate;
+
+struct _ClassesView {
+ GtkTreeView parent;
+ ClassesViewPrivate *priv;
+};
+
+struct _ClassesViewClass {
+ GtkTreeViewClass parent_class;
+};
+
+GType classes_view_get_type (void) G_GNUC_CONST;
+
+GtkWidget *classes_view_new (BrowserConnection *bcnc, const gchar *classname);
+const gchar *classes_view_get_current_class (ClassesView *classes_view);
+void classes_view_set_current_class (ClassesView *classes_view, const gchar *classname);
+
+G_END_DECLS
+
+#endif
diff --git a/tools/browser/ldap-browser/entry-properties.c b/tools/browser/ldap-browser/entry-properties.c
new file mode 100644
index 0000000..a94dc1c
--- /dev/null
+++ b/tools/browser/ldap-browser/entry-properties.c
@@ -0,0 +1,926 @@
+/*
+ * Copyright (C) 2011 The GNOME Foundation
+ *
+ * AUTHORS:
+ * Vivien Malerba <malerba gnome-db org>
+ *
+ * This Program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <glib/gi18n-lib.h>
+#include <string.h>
+#include <gtk/gtk.h>
+#include "entry-properties.h"
+#include "marshal.h"
+#include <time.h>
+
+struct _EntryPropertiesPrivate {
+ BrowserConnection *bcnc;
+
+ GtkTextView *view;
+ GtkTextBuffer *text;
+ gboolean hovering_over_link;
+
+ /* coordinates of mouse */
+ gint bx;
+ gint by;
+};
+
+static void entry_properties_class_init (EntryPropertiesClass *klass);
+static void entry_properties_init (EntryProperties *eprop, EntryPropertiesClass *klass);
+static void entry_properties_dispose (GObject *object);
+
+static GObjectClass *parent_class = NULL;
+
+/* signals */
+enum {
+ OPEN_DN,
+ OPEN_CLASS,
+ LAST_SIGNAL
+};
+
+gint entry_properties_signals [LAST_SIGNAL] = { 0, 0 };
+
+/*
+ * EntryProperties class implementation
+ */
+
+static void
+entry_properties_class_init (EntryPropertiesClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ parent_class = g_type_class_peek_parent (klass);
+
+ entry_properties_signals [OPEN_DN] =
+ g_signal_new ("open-dn",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (EntryPropertiesClass, open_dn),
+ NULL, NULL,
+ _ldap_marshal_VOID__STRING, G_TYPE_NONE,
+ 1, G_TYPE_STRING);
+ entry_properties_signals [OPEN_CLASS] =
+ g_signal_new ("open-class",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (EntryPropertiesClass, open_class),
+ NULL, NULL,
+ _ldap_marshal_VOID__STRING, G_TYPE_NONE,
+ 1, G_TYPE_STRING);
+ klass->open_dn = NULL;
+ klass->open_class = NULL;
+
+ object_class->dispose = entry_properties_dispose;
+}
+
+
+static void
+entry_properties_init (EntryProperties *eprop, G_GNUC_UNUSED EntryPropertiesClass *klass)
+{
+ eprop->priv = g_new0 (EntryPropertiesPrivate, 1);
+ eprop->priv->hovering_over_link = FALSE;
+}
+
+static void
+entry_properties_dispose (GObject *object)
+{
+ EntryProperties *eprop = (EntryProperties *) object;
+
+ /* free memory */
+ if (eprop->priv) {
+ if (eprop->priv->bcnc) {
+ g_object_unref (eprop->priv->bcnc);
+ }
+ g_free (eprop->priv);
+ eprop->priv = NULL;
+ }
+
+ parent_class->dispose (object);
+}
+
+GType
+entry_properties_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0)) {
+ static const GTypeInfo columns = {
+ sizeof (EntryPropertiesClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) entry_properties_class_init,
+ NULL,
+ NULL,
+ sizeof (EntryProperties),
+ 0,
+ (GInstanceInitFunc) entry_properties_init,
+ 0
+ };
+ type = g_type_register_static (GTK_TYPE_VBOX, "EntryProperties", &columns, 0);
+ }
+ return type;
+}
+
+static gboolean key_press_event (GtkWidget *text_view, GdkEventKey *event, EntryProperties *eprop);
+static gboolean event_after (GtkWidget *text_view, GdkEvent *ev, EntryProperties *eprop);
+static gboolean motion_notify_event (GtkWidget *text_view, GdkEventMotion *event, EntryProperties *eprop);
+static gboolean visibility_notify_event (GtkWidget *text_view, GdkEventVisibility *event, EntryProperties *eprop);
+static void populate_popup_cb (GtkWidget *text_view, GtkMenu *menu, EntryProperties *eprop);
+
+/**
+ * entry_properties_new:
+ *
+ * Returns: a new #GtkWidget
+ */
+GtkWidget *
+entry_properties_new (BrowserConnection *bcnc)
+{
+ EntryProperties *eprop;
+ g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), NULL);
+
+ eprop = ENTRY_PROPERTIES (g_object_new (ENTRY_PROPERTIES_TYPE, NULL));
+ eprop->priv->bcnc = g_object_ref (bcnc);
+
+ GtkWidget *sw;
+ sw = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw), GTK_SHADOW_NONE);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+ gtk_box_pack_start (GTK_BOX (eprop), sw, TRUE, TRUE, 0);
+
+ GtkWidget *textview;
+ textview = gtk_text_view_new ();
+ gtk_container_add (GTK_CONTAINER (sw), textview);
+ gtk_text_view_set_left_margin (GTK_TEXT_VIEW (textview), 5);
+ gtk_text_view_set_right_margin (GTK_TEXT_VIEW (textview), 5);
+ gtk_text_view_set_editable (GTK_TEXT_VIEW (textview), FALSE);
+ gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (textview), FALSE);
+ eprop->priv->text = gtk_text_view_get_buffer (GTK_TEXT_VIEW (textview));
+ eprop->priv->view = GTK_TEXT_VIEW (textview);
+ gtk_widget_show_all (sw);
+
+ gtk_text_buffer_create_tag (eprop->priv->text, "section",
+ "weight", PANGO_WEIGHT_BOLD,
+ "foreground", "blue", NULL);
+
+ gtk_text_buffer_create_tag (eprop->priv->text, "error",
+ "foreground", "red", NULL);
+
+ gtk_text_buffer_create_tag (eprop->priv->text, "data",
+ "left-margin", 20, NULL);
+
+ gtk_text_buffer_create_tag (eprop->priv->text, "convdata",
+ "style", PANGO_STYLE_ITALIC,
+ "background", "lightgray",
+ "left-margin", 20, NULL);
+
+ gtk_text_buffer_create_tag (eprop->priv->text, "starter",
+ "indent", -10,
+ "left-margin", 20, NULL);
+
+ g_signal_connect (textview, "key-press-event",
+ G_CALLBACK (key_press_event), eprop);
+ g_signal_connect (textview, "event-after",
+ G_CALLBACK (event_after), eprop);
+ g_signal_connect (textview, "motion-notify-event",
+ G_CALLBACK (motion_notify_event), eprop);
+ g_signal_connect (textview, "visibility-notify-event",
+ G_CALLBACK (visibility_notify_event), eprop);
+ g_signal_connect (textview, "populate-popup",
+ G_CALLBACK (populate_popup_cb), eprop);
+
+ entry_properties_set_dn (eprop, NULL);
+
+ return (GtkWidget*) eprop;
+}
+
+static void
+data_save_cb (GtkWidget *mitem, EntryProperties *eprop)
+{
+ GtkWidget *dialog;
+
+ dialog = gtk_file_chooser_dialog_new (_("Select the file to save data to"),
+ (GtkWindow*) gtk_widget_get_toplevel (GTK_WIDGET (eprop)),
+ GTK_FILE_CHOOSER_ACTION_SAVE,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
+ NULL);
+ if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) {
+ char *filename;
+ GValue *binvalue;
+ GError *lerror;
+ const GdaBinary *bin = NULL;
+
+ filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
+ binvalue = g_object_get_data (G_OBJECT (mitem), "binvalue");
+ if (binvalue)
+ bin = gda_value_get_binary (binvalue);
+ if (!bin || !g_file_set_contents (filename, (gchar*) bin->data,
+ bin->binary_length, &lerror)) {
+ browser_show_error ((GtkWindow*) gtk_widget_get_toplevel (GTK_WIDGET (eprop)),
+ _("Could not save data: %s"),
+ lerror && lerror->message ? lerror->message : _("No detail"));
+ g_clear_error (&lerror);
+ }
+ g_free (filename);
+ }
+ gtk_widget_destroy (dialog);
+}
+
+static void
+populate_popup_cb (G_GNUC_UNUSED GtkWidget *text_view, GtkMenu *menu, EntryProperties *eprop)
+{
+ GtkTextIter iter;
+ gtk_text_view_get_iter_at_position (eprop->priv->view, &iter, NULL,
+ eprop->priv->bx, eprop->priv->by);
+
+ GSList *tags = NULL, *tagp = NULL;
+
+ tags = gtk_text_iter_get_tags (&iter);
+ for (tagp = tags; tagp != NULL; tagp = tagp->next) {
+ GtkTextTag *tag = tagp->data;
+ GValue *binvalue;
+
+ binvalue = g_object_get_data (G_OBJECT (tag), "binvalue");
+ if (binvalue) {
+ GtkWidget *item;
+
+ item = gtk_separator_menu_item_new ();
+ gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
+ gtk_widget_show (item);
+
+ item = gtk_menu_item_new_with_label (_("Save"));
+ gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
+ g_signal_connect (G_OBJECT (item), "activate",
+ G_CALLBACK (data_save_cb), eprop);
+ g_object_set_data (G_OBJECT (item), "binvalue", binvalue);
+ gtk_widget_show (item);
+
+ break;
+ }
+ }
+
+ if (tags)
+ g_slist_free (tags);
+}
+
+static GdkCursor *hand_cursor = NULL;
+static GdkCursor *regular_cursor = NULL;
+
+/* Looks at all tags covering the position (x, y) in the text view,
+ * and if one of them is a link, change the cursor to the "hands" cursor
+ * typically used by web browsers.
+ */
+static void
+set_cursor_if_appropriate (GtkTextView *text_view, gint x, gint y, EntryProperties *eprop)
+{
+ GSList *tags = NULL, *tagp = NULL;
+ GtkTextIter iter;
+ gboolean hovering = FALSE;
+
+ gtk_text_view_get_iter_at_location (text_view, &iter, x, y);
+
+ tags = gtk_text_iter_get_tags (&iter);
+ for (tagp = tags; tagp != NULL; tagp = tagp->next) {
+ GtkTextTag *tag = tagp->data;
+
+ if (g_object_get_data (G_OBJECT (tag), "dn") ||
+ g_object_get_data (G_OBJECT (tag), "class")) {
+ hovering = TRUE;
+ break;
+ }
+ }
+
+ if (hovering != eprop->priv->hovering_over_link) {
+ eprop->priv->hovering_over_link = hovering;
+
+ if (eprop->priv->hovering_over_link) {
+ if (! hand_cursor)
+ hand_cursor = gdk_cursor_new (GDK_HAND2);
+ gdk_window_set_cursor (gtk_text_view_get_window (text_view,
+ GTK_TEXT_WINDOW_TEXT),
+ hand_cursor);
+ }
+ else {
+ if (!regular_cursor)
+ regular_cursor = gdk_cursor_new (GDK_XTERM);
+ gdk_window_set_cursor (gtk_text_view_get_window (text_view,
+ GTK_TEXT_WINDOW_TEXT),
+ regular_cursor);
+ }
+ }
+
+ if (tags)
+ g_slist_free (tags);
+}
+
+/*
+ * Also update the cursor image if the window becomes visible
+ * (e.g. when a window covering it got iconified).
+ */
+static gboolean
+visibility_notify_event (GtkWidget *text_view, G_GNUC_UNUSED GdkEventVisibility *event,
+ EntryProperties *eprop)
+{
+ gint wx, wy, bx, by;
+
+ gdk_window_get_pointer (gtk_widget_get_window (text_view), &wx, &wy, NULL);
+
+ gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (text_view),
+ GTK_TEXT_WINDOW_WIDGET,
+ wx, wy, &bx, &by);
+
+ set_cursor_if_appropriate (GTK_TEXT_VIEW (text_view), bx, by, eprop);
+
+ return FALSE;
+}
+
+/*
+ * Update the cursor image if the pointer moved.
+ */
+static gboolean
+motion_notify_event (GtkWidget *text_view, GdkEventMotion *event, EntryProperties *eprop)
+{
+ gint x, y;
+
+ gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (text_view),
+ GTK_TEXT_WINDOW_WIDGET,
+ event->x, event->y, &x, &y);
+
+ set_cursor_if_appropriate (GTK_TEXT_VIEW (text_view), x, y, eprop);
+
+ gdk_window_get_pointer (gtk_widget_get_window (text_view), NULL, NULL, NULL);
+
+ /* store coordinates */
+ eprop->priv->bx = x;
+ eprop->priv->by = y;
+
+ return FALSE;
+}
+
+/* Looks at all tags covering the position of iter in the text view,
+ * and if one of them is a link, follow it by showing the page identified
+ * by the data attached to it.
+ */
+static void
+follow_if_link (G_GNUC_UNUSED GtkWidget *text_view, GtkTextIter *iter, EntryProperties *eprop)
+{
+ GSList *tags = NULL, *tagp = NULL;
+
+ tags = gtk_text_iter_get_tags (iter);
+ for (tagp = tags; tagp != NULL; tagp = tagp->next) {
+ GtkTextTag *tag = tagp->data;
+ const gchar *dn;
+
+ dn = g_object_get_data (G_OBJECT (tag), "dn");
+ if (dn)
+ g_signal_emit (eprop, entry_properties_signals [OPEN_DN], 0, dn);
+ dn = g_object_get_data (G_OBJECT (tag), "class");
+ if (dn)
+ g_signal_emit (eprop, entry_properties_signals [OPEN_CLASS], 0, dn);
+ }
+
+ if (tags)
+ g_slist_free (tags);
+}
+
+
+/*
+ * Links can also be activated by clicking.
+ */
+static gboolean
+event_after (GtkWidget *text_view, GdkEvent *ev, EntryProperties *eprop)
+{
+ GtkTextIter start, end, iter;
+ GtkTextBuffer *buffer;
+ GdkEventButton *event;
+ gint x, y;
+
+ if (ev->type != GDK_BUTTON_RELEASE)
+ return FALSE;
+
+ event = (GdkEventButton *)ev;
+
+ if (event->button != 1)
+ return FALSE;
+
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view));
+
+ /* we shouldn't follow a link if the user has selected something */
+ gtk_text_buffer_get_selection_bounds (buffer, &start, &end);
+ if (gtk_text_iter_get_offset (&start) != gtk_text_iter_get_offset (&end))
+ return FALSE;
+
+ gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (text_view),
+ GTK_TEXT_WINDOW_WIDGET,
+ event->x, event->y, &x, &y);
+
+ gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW (text_view), &iter, x, y);
+
+ follow_if_link (text_view, &iter, eprop);
+
+ return FALSE;
+}
+
+/*
+ * Links can be activated by pressing Enter.
+ */
+static gboolean
+key_press_event (GtkWidget *text_view, GdkEventKey *event, EntryProperties *eprop)
+{
+ GtkTextIter iter;
+ GtkTextBuffer *buffer;
+
+ switch (event->keyval) {
+ case GDK_KEY_Return:
+ case GDK_KEY_KP_Enter:
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view));
+ gtk_text_buffer_get_iter_at_mark (buffer, &iter,
+ gtk_text_buffer_get_insert (buffer));
+ follow_if_link (text_view, &iter, eprop);
+ break;
+
+ default:
+ break;
+ }
+ return FALSE;
+}
+
+static GdkPixbuf *
+data_to_pixbuf (const GValue *cvalue)
+{
+ GdkPixbuf *retpixbuf = NULL;
+
+ if (G_VALUE_TYPE (cvalue) == GDA_TYPE_BINARY) {
+ const GdaBinary *bin;
+ GdkPixbufLoader *loader;
+
+ bin = gda_value_get_binary (cvalue);
+ if (!bin->data)
+ goto out;
+
+ loader = gdk_pixbuf_loader_new ();
+ if (gdk_pixbuf_loader_write (loader, bin->data, bin->binary_length, NULL)) {
+ if (gdk_pixbuf_loader_close (loader, NULL)) {
+ retpixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
+ g_object_ref (retpixbuf);
+ }
+ else
+ gdk_pixbuf_loader_close (loader, NULL);
+ }
+ else
+ gdk_pixbuf_loader_close (loader, NULL);
+ g_object_unref (loader);
+ }
+
+ out:
+ return retpixbuf;
+}
+
+static gchar *
+unix_shadow_to_string (const gchar *value, const gchar *attname)
+{
+ /* value is the number of days since 1970-01-01 */
+ gint64 i64;
+ gchar *endptr [1];
+
+ if (!value || !*value)
+ return NULL;
+
+ i64 = g_ascii_strtoll (value, endptr, 10);
+ if (**endptr != '\0')
+ return NULL;
+
+ if ((i64 == -1) &&
+ (!strcmp (attname, "shadowInactive") ||
+ !strcmp (attname, "shadowMin") ||
+ !strcmp (attname, "shadowExpire")))
+ return g_strdup (_("Non activated"));
+ else if ((i64 == 99999) && !strcmp (attname, "shadowMax"))
+ return g_strdup ("Always valid");
+ if ((i64 >= G_MAXUINT) || (i64 < 0))
+ return NULL;
+
+ if (!strcmp (attname, "shadowMax") ||
+ !strcmp (attname, "shadowMin") ||
+ !strcmp (attname, "shadowInactive"))
+ return NULL;
+
+ GDate *date;
+ date = g_date_new_dmy (1, 1, 1970);
+ g_date_add_days (date, (guint) i64);
+ if (! g_date_valid (date)) {
+ g_date_free (date);
+ return NULL;
+ }
+
+ GdaDataHandler *dh;
+ GValue tvalue;
+ gchar *str;
+
+ memset (&tvalue, 0, sizeof (GValue));
+ g_value_init (&tvalue, G_TYPE_DATE);
+ g_value_take_boxed (&tvalue, date);
+ dh = gda_data_handler_get_default (G_TYPE_DATE);
+ str = gda_data_handler_get_str_from_value (dh, &tvalue);
+ g_value_reset (&tvalue);
+
+ return str;
+}
+
+static gchar *
+ad_1601_timestamp_to_string (const gchar *value, const gchar *attname)
+{
+ /* value is the number of 100 nanoseconds since 1601-01-01 UTC */
+ gint64 i64;
+ gchar *endptr [1];
+
+ if (!value || !*value)
+ return NULL;
+
+ i64 = g_ascii_strtoll (value, endptr, 10);
+ if (**endptr != '\0')
+ return NULL;
+
+ if (i64 == 0x7FFFFFFFFFFFFFFF)
+ return g_strdup (_("Never"));
+
+ if (i64 == 0 && attname) {
+ if (!strcmp (attname, "accountExpires"))
+ return g_strdup (_("Never"));
+ else
+ return g_strdup (_("Unknown"));
+ }
+
+ i64 = (i64 / (guint64) 10000000);
+ if (i64 < (gint64) 11644473600)
+ return NULL;
+ i64 = i64 - (guint64) 11644473600;
+ if (i64 >= G_MAXINT)
+ return NULL;
+
+ GdaDataHandler *dh;
+ struct tm stm;
+ GValue tvalue;
+ GdaTimestamp ts;
+ time_t nsec = (time_t) i64;
+ gchar *str;
+
+ localtime_r (&nsec, &stm);
+ memset (&ts, 0, sizeof (GdaTimestamp));
+ ts.year = stm.tm_year + 1900;
+ ts.month = stm.tm_mon + 1;
+ ts.day = stm.tm_mday;
+ ts.hour = stm.tm_hour;
+ ts.minute = stm.tm_min;
+ ts.second = stm.tm_sec;
+ ts.timezone = GDA_TIMEZONE_INVALID;
+ memset (&tvalue, 0, sizeof (GValue));
+ gda_value_set_timestamp (&tvalue, &ts);
+ dh = gda_data_handler_get_default (GDA_TYPE_TIMESTAMP);
+ str = gda_data_handler_get_str_from_value (dh, &tvalue);
+ g_value_reset (&tvalue);
+
+ return str;
+}
+
+typedef struct {
+ guint mask;
+ gchar *descr;
+} ADUACData;
+
+ADUACData uac_data[] = {
+ {0x00000001, "Logon script is executed"},
+ {0x00000002, "Account disabled"},
+ {0x00000008, "Home directory required"},
+ {0x00000010, "Account locked out"},
+ {0x00000020, "No password required"},
+ {0x00000040, "User cannot change password"},
+ {0x00000080, "User can send an encrypted password"},
+ {0x00000100, "Duplicate account (local user account)"},
+ {0x00000200, "Default account type"},
+ {0x00000800, "Permit to trust account for a system domain that trusts other domains"},
+ {0x00001000, "Account for a computer"},
+ {0x00002000, "Account for a system backup domain controller"},
+ {0x00010000, "Account never expires"},
+ {0x00020000, "Majority Node Set (MNS) logon account"},
+ {0x00040000, "User must log on using a smart card"},
+ {0x00080000, "Service account for trusted for Kerberos delegation"},
+ {0x00100000, "Security context not delegated"},
+ {0x00200000, "Only Data Encryption Standard (DES) encryption for keys"},
+ {0x00400000, "Kerberos pre-authentication not required"},
+ {0x00800000, "User password expired"},
+ {0x01000000, "Account enabled for delegation"}
+};
+
+static gchar *
+ad_1601_uac_to_string (const gchar *value)
+{
+ gint64 i64;
+ gchar *endptr [1];
+
+ if (!value || !*value)
+ return NULL;
+
+ i64 = g_ascii_strtoll (value, endptr, 10);
+ if (**endptr != '\0')
+ return NULL;
+ if (i64 < 0)
+ return NULL;
+ if (i64 > G_MAXUINT32)
+ return NULL;
+
+ GString *string = NULL;
+ guint i;
+ guint32 v;
+ v = (guint32) i64;
+ for (i = 0; i < sizeof (uac_data) / sizeof (ADUACData); i++) {
+ ADUACData *d;
+ d = & (uac_data [i]);
+ if (v & d->mask) {
+ if (string)
+ g_string_append (string, " / ");
+ else
+ string = g_string_new ("");
+ g_string_append (string, d->descr);
+ }
+ }
+ if (string)
+ return g_string_free (string, FALSE);
+ else
+ return NULL;
+}
+
+static gchar *
+ad_sam_account_type_to_string (const gchar *value)
+{
+ gint64 i64;
+ gchar *endptr [1];
+
+ if (!value || !*value)
+ return NULL;
+
+ i64 = g_ascii_strtoll (value, endptr, 10);
+ if (**endptr != '\0')
+ return NULL;
+ if (i64 < 0)
+ return NULL;
+
+ switch (i64) {
+ case 0x0:
+ return g_strdup ("SAM_DOMAIN_OBJECT");
+ case 0x10000000:
+ return g_strdup ("SAM_GROUP_OBJECT");
+ case 0x10000001:
+ return g_strdup ("SAM_NON_SECURITY_GROUP_OBJECT");
+ case 0x20000000:
+ return g_strdup ("SAM_ALIAS_OBJECT");
+ case 0x20000001:
+ return g_strdup ("SAM_NON_SECURITY_ALIAS_OBJECT");
+ case 0x30000000:
+ return g_strdup ("SAM_NORMAL_USER_ACCOUNT");
+ case 0x30000001:
+ return g_strdup ("SAM_MACHINE_ACCOUNT");
+ case 0x30000002:
+ return g_strdup ("SAM_TRUST_ACCOUNT");
+ case 0x40000000:
+ return g_strdup ("SAM_APP_BASIC_GROUP");
+ case 0x40000001:
+ return g_strdup ("SAM_APP_QUERY_GROUP");
+ case 0x7fffffff:
+ return g_strdup ("SAM_ACCOUNT_TYPE_MAX");
+ default:
+ return NULL;
+ }
+}
+
+static void
+info_fetch_cb (BrowserConnection *bcnc, gpointer out_result, EntryProperties *eprop, G_GNUC_UNUSED GError *error)
+{
+ if (out_result) {
+ GtkTextBuffer *tbuffer;
+ GtkTextIter start, end;
+
+ tbuffer = eprop->priv->text;
+ gtk_text_buffer_get_start_iter (tbuffer, &start);
+ gtk_text_buffer_get_end_iter (tbuffer, &end);
+ gtk_text_buffer_delete (tbuffer, &start, &end);
+
+ GdaLdapEntry *entry = (GdaLdapEntry*) out_result;
+ guint i;
+ GtkTextIter current;
+
+ gtk_text_buffer_get_start_iter (tbuffer, ¤t);
+
+ /* DN */
+ gtk_text_buffer_insert_with_tags_by_name (tbuffer, ¤t, _("Distinguished Name:"), -1,
+ "section", NULL);
+ gtk_text_buffer_insert (tbuffer, ¤t, "\n", -1);
+ gtk_text_buffer_insert_with_tags_by_name (tbuffer, ¤t, " ", -1, "starter", NULL);
+ gtk_text_buffer_insert_with_tags_by_name (tbuffer, ¤t, entry->dn, -1,
+ "data", NULL);
+ gtk_text_buffer_insert (tbuffer, ¤t, "\n", -1);
+
+ /* other attributes */
+ const gchar *basedn;
+ GdaDataHandler *ts_dh = NULL;
+ basedn = browser_connection_ldap_get_base_dn (bcnc);
+
+ for (i = 0; i < entry->nb_attributes; i++) {
+ GdaLdapAttribute *attr;
+ gchar *tmp;
+ attr = entry->attributes [i];
+ tmp = g_strdup_printf ("%s:", attr->attr_name);
+ gtk_text_buffer_insert_with_tags_by_name (tbuffer, ¤t, tmp, -1, "section", NULL);
+ g_free (tmp);
+ gtk_text_buffer_insert (tbuffer, ¤t, "\n", -1);
+
+ guint j;
+ for (j = 0; j < attr->nb_values; j++) {
+ const GValue *cvalue;
+ cvalue = attr->values [j];
+
+ gtk_text_buffer_insert_with_tags_by_name (tbuffer, ¤t, " ", -1,
+ "starter", NULL);
+
+ if (G_VALUE_TYPE (cvalue) == GDA_TYPE_BINARY) {
+ GValue *copyvalue;
+ GtkTextTagTable *table;
+ GtkTextTag *tag;
+ GtkTextMark *mark;
+
+ copyvalue = gda_value_copy (cvalue);
+ table = gtk_text_buffer_get_tag_table (tbuffer);
+ tag = gtk_text_tag_new (NULL);
+ gtk_text_tag_table_add (table, tag);
+ g_object_set_data_full ((GObject*) tag, "binvalue",
+ copyvalue, (GDestroyNotify) gda_value_free);
+ g_object_unref ((GObject*) tag);
+
+ mark = gtk_text_buffer_create_mark (tbuffer, NULL, ¤t, TRUE);
+
+ GdkPixbuf *pixbuf;
+ pixbuf = data_to_pixbuf (cvalue);
+ if (pixbuf) {
+ gtk_text_buffer_insert_pixbuf (tbuffer, ¤t, pixbuf);
+ g_object_unref (pixbuf);
+ }
+ else {
+ GdaDataHandler *dh;
+ dh = gda_data_handler_get_default (G_VALUE_TYPE (cvalue));
+ if (dh)
+ tmp = gda_data_handler_get_str_from_value (dh, cvalue);
+ else
+ tmp = gda_value_stringify (cvalue);
+ gtk_text_buffer_insert_with_tags_by_name (tbuffer, ¤t,
+ tmp, -1,
+ "data", NULL);
+ g_free (tmp);
+ }
+ GtkTextIter before;
+ gtk_text_buffer_get_iter_at_mark (tbuffer, &before, mark);
+ gtk_text_buffer_apply_tag (tbuffer, tag, &before, ¤t);
+ gtk_text_buffer_delete_mark (tbuffer, mark);
+
+ gtk_text_buffer_insert_with_tags_by_name (tbuffer, ¤t,
+ "\n", 1,
+ "data", NULL);
+ }
+ else {
+ GdaDataHandler *dh;
+ dh = gda_data_handler_get_default (G_VALUE_TYPE (cvalue));
+ if (dh)
+ tmp = gda_data_handler_get_str_from_value (dh, cvalue);
+ else
+ tmp = gda_value_stringify (cvalue);
+ if (tmp) {
+ if (*tmp &&
+ ((basedn && g_str_has_suffix (tmp, basedn)) || !basedn) &&
+ gda_ldap_is_dn (tmp)) {
+ /* we have a DN */
+ GtkTextTag *tag;
+ tag = gtk_text_buffer_create_tag (tbuffer, NULL,
+ "foreground", "blue",
+ "weight", PANGO_WEIGHT_NORMAL,
+ "underline", PANGO_UNDERLINE_SINGLE,
+ NULL);
+ g_object_set_data_full (G_OBJECT (tag), "dn",
+ g_strdup (tmp), g_free);
+ gtk_text_buffer_insert_with_tags (tbuffer, ¤t,
+ tmp, -1,
+ tag, NULL);
+ }
+ else if (attr->attr_name &&
+ !g_ascii_strcasecmp (attr->attr_name, "objectClass")) {
+ GtkTextTag *tag;
+ tag = gtk_text_buffer_create_tag (tbuffer, NULL,
+ "foreground", "blue",
+ "weight", PANGO_WEIGHT_NORMAL,
+ "underline", PANGO_UNDERLINE_SINGLE,
+ NULL);
+ g_object_set_data_full (G_OBJECT (tag), "class",
+ g_strdup (tmp), g_free);
+ gtk_text_buffer_insert_with_tags (tbuffer, ¤t,
+ tmp, -1,
+ tag, NULL);
+ }
+ else
+ gtk_text_buffer_insert_with_tags_by_name (tbuffer, ¤t, tmp, -1,
+ "data", NULL);
+
+ gchar *extrainfo = NULL;
+ if (!strncmp (attr->attr_name, "shadow", 6) &&
+ (!strcmp (attr->attr_name, "shadowLastChange") ||
+ !strcmp (attr->attr_name, "shadowMax") ||
+ !strcmp (attr->attr_name, "shadowMin") ||
+ !strcmp (attr->attr_name, "shadowInactive") ||
+ !strcmp (attr->attr_name, "shadowExpire")))
+ extrainfo = unix_shadow_to_string (tmp, attr->attr_name);
+ else if (!strcmp (attr->attr_name, "badPasswordTime") ||
+ !strcmp (attr->attr_name, "lastLogon") ||
+ !strcmp (attr->attr_name, "pwdLastSet") ||
+ !strcmp (attr->attr_name, "accountExpires") ||
+ !strcmp (attr->attr_name, "lockoutTime") ||
+ !strcmp (attr->attr_name, "lastLogonTimestamp"))
+ extrainfo = ad_1601_timestamp_to_string (tmp, attr->attr_name);
+ else if (!strcmp (attr->attr_name, "userAccountControl"))
+ extrainfo = ad_1601_uac_to_string (tmp);
+ else if (!strcmp (attr->attr_name, "sAMAccountType"))
+ extrainfo = ad_sam_account_type_to_string (tmp);
+
+ if (extrainfo) {
+ gtk_text_buffer_insert_with_tags_by_name (tbuffer, ¤t,
+ " ", 1,
+ "data", NULL);
+ gtk_text_buffer_insert_with_tags_by_name (tbuffer, ¤t,
+ extrainfo, -1,
+ "convdata", NULL);
+ g_free (extrainfo);
+ }
+ g_free (tmp);
+ }
+ else {
+ gtk_text_buffer_insert_with_tags_by_name (tbuffer, ¤t, _("Can't display attribute value"), -1,
+ "error", NULL);
+ }
+ gtk_text_buffer_insert_with_tags_by_name (tbuffer, ¤t,
+ "\n", 1,
+ "data", NULL);
+ }
+ }
+ }
+ if (ts_dh)
+ g_object_unref (ts_dh);
+ gda_ldap_entry_free (entry);
+ }
+ else
+ browser_show_message (GTK_WINDOW (gtk_widget_get_toplevel ((GtkWidget*) eprop)),
+ "%s", _("Could not get information about LDAP entry"));
+
+ g_object_unref (eprop);
+}
+
+/**
+ * entry_properties_set_dn:
+ * @eprop: a #EntryProperties widget
+ * @dn: a DN to display information for
+ *
+ * Adjusts the display to show @dn's properties
+ */
+void
+entry_properties_set_dn (EntryProperties *eprop, const gchar *dn)
+{
+ g_return_if_fail (IS_ENTRY_PROPERTIES (eprop));
+
+ GtkTextBuffer *tbuffer;
+ GtkTextIter start, end;
+
+ tbuffer = eprop->priv->text;
+ gtk_text_buffer_get_start_iter (tbuffer, &start);
+ gtk_text_buffer_get_end_iter (tbuffer, &end);
+ gtk_text_buffer_delete (tbuffer, &start, &end);
+
+ if (dn && *dn) {
+ guint id;
+ id = browser_connection_ldap_describe_entry (eprop->priv->bcnc, dn,
+ (BrowserConnectionJobCallback) info_fetch_cb,
+ g_object_ref (eprop), NULL);
+ if (id == 0)
+ browser_show_message (GTK_WINDOW (gtk_widget_get_toplevel ((GtkWidget*) eprop)),
+ "%s", _("Could not get information about LDAP entry"));
+ }
+}
diff --git a/tools/browser/ldap-browser/entry-properties.h b/tools/browser/ldap-browser/entry-properties.h
new file mode 100644
index 0000000..80cf885
--- /dev/null
+++ b/tools/browser/ldap-browser/entry-properties.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2011 The GNOME Foundation
+ *
+ * AUTHORS:
+ * Vivien Malerba <malerba gnome-db org>
+ *
+ * This Program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ENTRY_PROPERTIES_H__
+#define __ENTRY_PROPERTIES_H__
+
+#include "../browser-connection.h"
+
+G_BEGIN_DECLS
+
+#define ENTRY_PROPERTIES_TYPE (entry_properties_get_type())
+#define ENTRY_PROPERTIES(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, ENTRY_PROPERTIES_TYPE, EntryProperties))
+#define ENTRY_PROPERTIES_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, ENTRY_PROPERTIES_TYPE, EntryPropertiesClass))
+#define IS_ENTRY_PROPERTIES(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, ENTRY_PROPERTIES_TYPE))
+#define IS_ENTRY_PROPERTIES_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ENTRY_PROPERTIES_TYPE))
+
+typedef struct _EntryProperties EntryProperties;
+typedef struct _EntryPropertiesClass EntryPropertiesClass;
+typedef struct _EntryPropertiesPrivate EntryPropertiesPrivate;
+
+struct _EntryProperties {
+ GtkVBox parent;
+ EntryPropertiesPrivate *priv;
+};
+
+struct _EntryPropertiesClass {
+ GtkVBoxClass parent_class;
+
+ /* signals */
+ void (*open_dn) (EntryProperties *eprop, const gchar *dn);
+ void (*open_class) (EntryProperties *eprop, const gchar *classname);
+};
+
+GType entry_properties_get_type (void) G_GNUC_CONST;
+
+GtkWidget *entry_properties_new (BrowserConnection *bcnc);
+void entry_properties_set_dn (EntryProperties *eprop, const gchar *dn);
+
+G_END_DECLS
+
+#endif
diff --git a/tools/browser/ldap-browser/filter-editor.c b/tools/browser/ldap-browser/filter-editor.c
new file mode 100644
index 0000000..14a1328
--- /dev/null
+++ b/tools/browser/ldap-browser/filter-editor.c
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2011 The GNOME Foundation
+ *
+ * AUTHORS:
+ * Vivien Malerba <malerba gnome-db org>
+ *
+ * This Program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <glib/gi18n-lib.h>
+#include <string.h>
+#include "filter-editor.h"
+#include <libgda-ui/gdaui-combo.h>
+#include <libgda-ui/gdaui-data-selector.h>
+
+struct _FilterEditorPrivate {
+ BrowserConnection *bcnc;
+ GtkWidget *base_dn;
+ GtkWidget *filter;
+ GtkWidget *attributes;
+ GtkWidget *scope;
+
+ GdaLdapSearchScope default_scope;
+};
+
+static void filter_editor_class_init (FilterEditorClass *klass);
+static void filter_editor_init (FilterEditor *feditor, FilterEditorClass *klass);
+static void filter_editor_dispose (GObject *object);
+
+static GObjectClass *parent_class = NULL;
+
+
+/*
+ * FilterEditor class implementation
+ */
+
+static void
+filter_editor_class_init (FilterEditorClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ object_class->dispose = filter_editor_dispose;
+}
+
+static void
+filter_editor_init (FilterEditor *feditor, G_GNUC_UNUSED FilterEditorClass *klass)
+{
+ feditor->priv = g_new0 (FilterEditorPrivate, 1);
+ feditor->priv->bcnc = NULL;
+ feditor->priv->default_scope = GDA_LDAP_SEARCH_SUBTREE;
+}
+
+static void
+filter_editor_dispose (GObject *object)
+{
+ FilterEditor *feditor = (FilterEditor *) object;
+
+ /* free memory */
+ if (feditor->priv) {
+ if (feditor->priv->bcnc)
+ g_object_unref (feditor->priv->bcnc);
+ g_free (feditor->priv);
+ feditor->priv = NULL;
+ }
+
+ parent_class->dispose (object);
+}
+
+GType
+filter_editor_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0)) {
+ static const GTypeInfo info = {
+ sizeof (FilterEditorClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) filter_editor_class_init,
+ NULL,
+ NULL,
+ sizeof (FilterEditor),
+ 0,
+ (GInstanceInitFunc) filter_editor_init,
+ 0
+ };
+
+ type = g_type_register_static (GTK_TYPE_VBOX, "FilterEditor", &info, 0);
+ }
+ return type;
+}
+
+/**
+ * filter_editor_new:
+ *
+ * Returns: a new #GtkWidget
+ */
+GtkWidget *
+filter_editor_new (BrowserConnection *bcnc)
+{
+ FilterEditor *feditor;
+ GtkWidget *table, *label, *entry;
+ GdaDataModel *model;
+ GList *values;
+ GValue *v1, *v2;
+ gfloat ya;
+
+ g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), NULL);
+
+ feditor = FILTER_EDITOR (g_object_new (FILTER_EDITOR_TYPE, NULL));
+ feditor->priv->bcnc = g_object_ref ((GObject*) bcnc);
+
+ table = gtk_table_new (4, 2, FALSE);
+ gtk_table_set_col_spacing (GTK_TABLE (table), 0, 5);
+ gtk_box_pack_start (GTK_BOX (feditor), table, TRUE, TRUE, 0);
+
+ label = gtk_label_new (_("Base DN:"));
+ gtk_misc_get_alignment (GTK_MISC (label), NULL, &ya);
+ gtk_misc_set_alignment (GTK_MISC (label), 0., ya);
+ gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1, GTK_FILL, GTK_SHRINK, 0, 0);
+ label = gtk_label_new (_("Filter expression:"));
+ gtk_misc_get_alignment (GTK_MISC (label), NULL, &ya);
+ gtk_misc_set_alignment (GTK_MISC (label), 0., ya);
+ gtk_table_attach (GTK_TABLE (table), label, 0, 1, 1, 2, GTK_FILL, GTK_SHRINK, 0, 0);
+ label = gtk_label_new (_("Attributes to fetch:"));
+ gtk_misc_get_alignment (GTK_MISC (label), NULL, &ya);
+ gtk_misc_set_alignment (GTK_MISC (label), 0., ya);
+ gtk_table_attach (GTK_TABLE (table), label, 0, 1, 2, 3, GTK_FILL, GTK_SHRINK, 0, 0);
+ label = gtk_label_new (_("Search scope:"));
+ gtk_misc_get_alignment (GTK_MISC (label), NULL, &ya);
+ gtk_misc_set_alignment (GTK_MISC (label), 0., ya);
+ gtk_table_attach (GTK_TABLE (table), label, 0, 1, 3, 4, GTK_FILL, GTK_SHRINK, 0, 0);
+
+ entry = gtk_entry_new ();
+ gtk_table_attach_defaults (GTK_TABLE (table), entry, 1, 2, 0, 1);
+ feditor->priv->base_dn = entry;
+
+ entry = gtk_entry_new ();
+ gtk_table_attach_defaults (GTK_TABLE (table), entry, 1, 2, 1, 2);
+ feditor->priv->filter = entry;
+
+ entry = gtk_entry_new ();
+ gtk_table_attach_defaults (GTK_TABLE (table), entry, 1, 2, 2, 3);
+ feditor->priv->attributes = entry;
+
+ model = gda_data_model_array_new_with_g_types (2, G_TYPE_INT, G_TYPE_STRING);
+ g_value_set_string ((v1 = gda_value_new (G_TYPE_STRING)), "Base (search the base DN only)");
+ values = g_list_prepend (NULL, v1);
+ g_value_set_int ((v2 = gda_value_new (G_TYPE_INT)), GDA_LDAP_SEARCH_BASE);
+ values = g_list_prepend (values, v2);
+ g_assert (gda_data_model_append_values (model, values, NULL) >= 0);
+ gda_value_free (v1);
+ gda_value_free (v2);
+
+ g_value_set_string ((v1 = gda_value_new (G_TYPE_STRING)), "Onelevel (search immediate children of base DN only)");
+ values = g_list_prepend (NULL, v1);
+ g_value_set_int ((v2 = gda_value_new (G_TYPE_INT)), GDA_LDAP_SEARCH_ONELEVEL);
+ values = g_list_prepend (values, v2);
+ g_assert (gda_data_model_append_values (model, values, NULL) >= 0);
+ gda_value_free (v1);
+ gda_value_free (v2);
+
+ g_value_set_string ((v1 = gda_value_new (G_TYPE_STRING)), "Subtree (search of the base DN and the entire subtree below)");
+ values = g_list_prepend (NULL, v1);
+ g_value_set_int ((v2 = gda_value_new (G_TYPE_INT)), GDA_LDAP_SEARCH_SUBTREE);
+ values = g_list_prepend (values, v2);
+ g_assert (gda_data_model_append_values (model, values, NULL) >= 0);
+ gda_value_free (v1);
+ gda_value_free (v2);
+
+ gint cols[] = {1};
+ entry = gdaui_combo_new_with_model (model, 1, cols);
+ g_object_unref (model);
+ gtk_table_attach_defaults (GTK_TABLE (table), entry, 1, 2, 3, 4);
+ feditor->priv->scope = entry;
+ filter_editor_clear (feditor);
+
+ gtk_widget_show_all (table);
+ return (GtkWidget*) feditor;
+}
+
+/**
+ * filter_editor_clear:
+ */
+void
+filter_editor_clear (FilterEditor *fedit)
+{
+ g_return_if_fail (IS_FILTER_EDITOR (fedit));
+ filter_editor_set_settings (fedit, NULL, NULL, NULL, fedit->priv->default_scope);
+}
+
+/**
+ * filter_editor_set_settings:
+ */
+void
+filter_editor_set_settings (FilterEditor *fedit,
+ const gchar *base_dn, const gchar *filter,
+ const gchar *attributes, GdaLdapSearchScope scope)
+{
+ g_return_if_fail (IS_FILTER_EDITOR (fedit));
+
+ gtk_entry_set_text (GTK_ENTRY (fedit->priv->base_dn), base_dn ? base_dn : "");
+ gtk_entry_set_text (GTK_ENTRY (fedit->priv->filter), filter ? filter : "");
+ gtk_entry_set_text (GTK_ENTRY (fedit->priv->attributes), attributes ? attributes : "");
+ gdaui_data_selector_select_row (GDAUI_DATA_SELECTOR (fedit->priv->scope), scope - 1);
+}
+
+/**
+ * filter_editor_get_settings:
+ */
+void
+filter_editor_get_settings (FilterEditor *fedit,
+ gchar **out_base_dn, gchar **out_filter,
+ gchar **out_attributes, GdaLdapSearchScope *out_scope)
+{
+ const gchar *tmp;
+ g_return_if_fail (IS_FILTER_EDITOR (fedit));
+ if (out_base_dn) {
+ tmp = gtk_entry_get_text (GTK_ENTRY (fedit->priv->base_dn));
+ *out_base_dn = tmp && *tmp ? g_strdup (tmp) : NULL;
+ }
+ if (out_filter) {
+ tmp = gtk_entry_get_text (GTK_ENTRY (fedit->priv->filter));
+ if (tmp && *tmp) {
+ /* add surrounding parenthesis if not yet there */
+ if (*tmp != '(') {
+ gint len;
+ len = strlen (tmp);
+ if (tmp [len-1] != ')')
+ *out_filter = g_strdup_printf ("(%s)", tmp);
+ else
+ *out_filter = g_strdup (tmp);/* may result in an error when executed */
+ }
+ else
+ *out_filter = g_strdup (tmp);
+
+ }
+ else
+ *out_filter = NULL;
+ }
+ if (out_attributes) {
+ tmp = gtk_entry_get_text (GTK_ENTRY (fedit->priv->attributes));
+ *out_attributes = tmp && *tmp ? g_strdup (tmp) : NULL;
+ }
+ if (out_scope)
+ *out_scope = gtk_combo_box_get_active (GTK_COMBO_BOX (fedit->priv->scope)) + 1;
+}
diff --git a/tools/browser/ldap-browser/filter-editor.h b/tools/browser/ldap-browser/filter-editor.h
new file mode 100644
index 0000000..fcfbd72
--- /dev/null
+++ b/tools/browser/ldap-browser/filter-editor.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2011 The GNOME Foundation
+ *
+ * AUTHORS:
+ * Vivien Malerba <malerba gnome-db org>
+ *
+ * This Program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __FILTER_EDITOR_H__
+#define __FILTER_EDITOR_H__
+
+#include <gtk/gtk.h>
+#include <libgda/libgda.h>
+#include "../browser-connection.h"
+
+G_BEGIN_DECLS
+
+#define FILTER_EDITOR_TYPE (filter_editor_get_type())
+#define FILTER_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, FILTER_EDITOR_TYPE, FilterEditor))
+#define FILTER_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, FILTER_EDITOR_TYPE, FilterEditorClass))
+#define IS_FILTER_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, FILTER_EDITOR_TYPE))
+#define IS_FILTER_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FILTER_EDITOR_TYPE))
+
+typedef struct _FilterEditor FilterEditor;
+typedef struct _FilterEditorClass FilterEditorClass;
+typedef struct _FilterEditorPrivate FilterEditorPrivate;
+
+struct _FilterEditor {
+ GtkVBox parent;
+ FilterEditorPrivate *priv;
+};
+
+struct _FilterEditorClass {
+ GtkVBoxClass parent_class;
+};
+
+GType filter_editor_get_type (void) G_GNUC_CONST;
+
+GtkWidget *filter_editor_new (BrowserConnection *bcnc);
+void filter_editor_clear (FilterEditor *fedit);
+void filter_editor_set_settings (FilterEditor *fedit,
+ const gchar *base_dn, const gchar *filter,
+ const gchar *attributes, GdaLdapSearchScope scope);
+void filter_editor_get_settings (FilterEditor *fedit,
+ gchar **out_base_dn, gchar **out_filter,
+ gchar **out_attributes, GdaLdapSearchScope *out_scope);
+
+G_END_DECLS
+
+#endif
diff --git a/tools/browser/ldap-browser/hierarchy-view.c b/tools/browser/ldap-browser/hierarchy-view.c
new file mode 100644
index 0000000..a2870bd
--- /dev/null
+++ b/tools/browser/ldap-browser/hierarchy-view.c
@@ -0,0 +1,626 @@
+/*
+ * Copyright (C) 2011 The GNOME Foundation
+ *
+ * AUTHORS:
+ * Vivien Malerba <malerba gnome-db org>
+ *
+ * This Program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <glib/gi18n-lib.h>
+#include <string.h>
+#include "hierarchy-view.h"
+#include "../dnd.h"
+#include "../support.h"
+#include "../cc-gray-bar.h"
+#include "../browser-stock-icons.h"
+#include <virtual/gda-ldap-connection.h>
+#include "mgr-ldap-entries.h"
+#include <libgda-ui/gdaui-tree-store.h>
+
+struct _HierarchyViewPrivate {
+ BrowserConnection *bcnc;
+
+ GdaTree *ldap_tree;
+ GdauiTreeStore *ldap_store;
+
+ gchar *current_dn;
+ gchar *current_cn;
+
+ GArray *to_show;
+};
+
+static void hierarchy_view_class_init (HierarchyViewClass *klass);
+static void hierarchy_view_init (HierarchyView *eview, HierarchyViewClass *klass);
+static void hierarchy_view_dispose (GObject *object);
+static void hierarchy_view_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void hierarchy_view_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+/* properties */
+enum {
+ PROP_0,
+};
+
+static GObjectClass *parent_class = NULL;
+
+
+/*
+ * HierarchyView class implementation
+ */
+
+static void
+hierarchy_view_class_init (HierarchyViewClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ /* Properties */
+ object_class->set_property = hierarchy_view_set_property;
+ object_class->get_property = hierarchy_view_get_property;
+
+ object_class->dispose = hierarchy_view_dispose;
+}
+
+static void
+hierarchy_view_init (HierarchyView *eview, G_GNUC_UNUSED HierarchyViewClass *klass)
+{
+ eview->priv = g_new0 (HierarchyViewPrivate, 1);
+ eview->priv->current_dn = NULL;
+ eview->priv->current_cn = NULL;
+}
+
+static void
+hierarchy_view_dispose (GObject *object)
+{
+ HierarchyView *eview = (HierarchyView *) object;
+
+ /* free memory */
+ if (eview->priv) {
+ if (eview->priv->bcnc)
+ g_object_unref (eview->priv->bcnc);
+ if (eview->priv->ldap_tree)
+ g_object_unref (eview->priv->ldap_tree);
+ if (eview->priv->to_show) {
+ guint i;
+ for (i = 0; i < eview->priv->to_show->len; i++) {
+ gchar *tmp;
+ tmp = (gchar*) g_array_index (eview->priv->to_show, gchar*, i);
+ g_free (tmp);
+ }
+ g_array_free (eview->priv->to_show, TRUE);
+ }
+
+ g_free (eview->priv);
+ eview->priv = NULL;
+ }
+
+ parent_class->dispose (object);
+}
+
+GType
+hierarchy_view_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0)) {
+ static const GTypeInfo info = {
+ sizeof (HierarchyViewClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) hierarchy_view_class_init,
+ NULL,
+ NULL,
+ sizeof (HierarchyView),
+ 0,
+ (GInstanceInitFunc) hierarchy_view_init,
+ 0
+ };
+
+ type = g_type_register_static (GTK_TYPE_TREE_VIEW, "HierarchyView", &info, 0);
+ }
+ return type;
+}
+
+static void
+hierarchy_view_set_property (GObject *object,
+ guint param_id,
+ G_GNUC_UNUSED const GValue *value,
+ GParamSpec *pspec)
+{
+ HierarchyView *eview;
+ eview = HIERARCHY_VIEW (object);
+ switch (param_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+static void
+hierarchy_view_get_property (GObject *object,
+ guint param_id,
+ G_GNUC_UNUSED GValue *value,
+ GParamSpec *pspec)
+{
+ HierarchyView *eview;
+ eview = HIERARCHY_VIEW (object);
+ switch (param_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+static gchar *
+hierarchy_view_to_selection (G_GNUC_UNUSED HierarchyView *eview)
+{
+ /*
+ GString *string;
+ gchar *tmp;
+ string = g_string_new ("OBJ_TYPE=hierarchy");
+ tmp = gda_rfc1738_encode (eview->priv->schema);
+ g_string_append_printf (string, ";OBJ_SCHEMA=%s", tmp);
+ g_free (tmp);
+ tmp = gda_rfc1738_encode (eview->priv->hierarchy_name);
+ g_string_append_printf (string, ";OBJ_NAME=%s", tmp);
+ g_free (tmp);
+ tmp = gda_rfc1738_encode (eview->priv->hierarchy_short_name);
+ g_string_append_printf (string, ";OBJ_SHORT_NAME=%s", tmp);
+ g_free (tmp);
+ return g_string_free (string, FALSE);
+ */
+ if (eview->priv->current_dn)
+ return g_strdup (eview->priv->current_dn);
+ else
+ return NULL;
+}
+
+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, HierarchyView *eview)
+{
+ switch (view) {
+ case TARGET_KEY_VALUE: {
+ gchar *str;
+ str = hierarchy_view_to_selection (eview);
+ gtk_selection_data_set (selection_data,
+ gtk_selection_data_get_target (selection_data), 8, (guchar*) str,
+ strlen (str));
+ g_free (str);
+ break;
+ }
+ default:
+ case TARGET_PLAIN: {
+ gtk_selection_data_set_text (selection_data, hierarchy_view_get_current_dn (eview, NULL), -1);
+ break;
+ }
+ case TARGET_ROOTWIN:
+ TO_IMPLEMENT; /* dropping on the Root Window => create a file */
+ break;
+ }
+}
+
+static gboolean test_expand_row_cb (GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, HierarchyView *eview);
+static void selection_changed_cb (GtkTreeSelection *sel, HierarchyView *eview);
+static gboolean make_dn_hierarchy (const gchar *basedn, const gchar *dn, GArray *in);
+static void go_to_row (HierarchyView *eview, GtkTreePath *path);
+
+enum
+{
+ COLUMN_RDN,
+ COLUMN_ICON,
+ COLUMN_DN,
+ COLUMN_CN,
+ NUM_COLUMNS
+};
+
+static void
+text_cell_data_func (GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
+ GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
+{
+ gchar *rdn, *cn;
+ gtk_tree_model_get (tree_model, iter,
+ COLUMN_RDN, &rdn, COLUMN_CN, &cn, -1);
+ g_object_set ((GObject*) cell, "text", cn && *cn ? cn : rdn, NULL);
+ g_free (cn);
+ g_free (rdn);
+}
+
+/**
+ * hierarchy_view_new
+ *
+ * Returns: a new #GtkWidget
+ */
+GtkWidget *
+hierarchy_view_new (BrowserConnection *bcnc, const gchar *dn)
+{
+ HierarchyView *eview;
+
+ g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), NULL);
+
+ eview = HIERARCHY_VIEW (g_object_new (HIERARCHY_VIEW_TYPE, NULL));
+ eview->priv->bcnc = g_object_ref ((GObject*) bcnc);
+ g_signal_connect (eview, "drag-data-get",
+ G_CALLBACK (source_drag_data_get_cb), eview);
+
+ GdaTreeManager *mgr;
+ GtkTreeModel *store;
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+ eview->priv->ldap_tree = gda_tree_new ();
+ mgr = mgr_ldap_entries_new (eview->priv->bcnc, NULL);
+ gda_tree_add_manager (eview->priv->ldap_tree, mgr);
+ gda_tree_manager_add_manager (mgr, mgr);
+ gda_tree_update_children (eview->priv->ldap_tree, NULL, NULL);
+ g_object_unref (mgr);
+
+ store = gdaui_tree_store_new (eview->priv->ldap_tree, NUM_COLUMNS,
+ G_TYPE_STRING, "rdn",
+ G_TYPE_OBJECT, "icon",
+ G_TYPE_STRING, "dn",
+ G_TYPE_STRING, "cn");
+ gtk_tree_view_set_model (GTK_TREE_VIEW (eview), GTK_TREE_MODEL (store));
+
+ eview->priv->ldap_store = GDAUI_TREE_STORE (store);
+ g_object_unref (G_OBJECT (store));
+ g_signal_connect (eview, "test-expand-row",
+ G_CALLBACK (test_expand_row_cb), eview);
+
+ column = gtk_tree_view_column_new ();
+
+ renderer = gtk_cell_renderer_pixbuf_new ();
+ gtk_tree_view_column_pack_start (column, renderer, FALSE);
+ gtk_tree_view_column_add_attribute (column, renderer, "pixbuf", COLUMN_ICON);
+ g_object_set ((GObject*) renderer, "yalign", 0., NULL);
+
+ renderer = gtk_cell_renderer_text_new ();
+ gtk_tree_view_column_pack_start (column, renderer, TRUE);
+ gtk_tree_view_column_set_cell_data_func (column, renderer,
+ (GtkTreeCellDataFunc) text_cell_data_func,
+ NULL, NULL);
+ //gtk_tree_view_column_add_attribute (column, renderer, "text", COLUMN_RDN);
+
+ gtk_tree_view_append_column (GTK_TREE_VIEW (eview), column);
+ gtk_tree_view_set_expander_column (GTK_TREE_VIEW (eview), column);
+ gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (eview), FALSE);
+
+ /* tree selection */
+ GtkTreeSelection *sel;
+ sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (eview));
+ gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
+ g_signal_connect (sel, "changed",
+ G_CALLBACK (selection_changed_cb), eview);
+
+ if (dn) {
+ const gchar *basedn;
+ GArray *array;
+
+ basedn = browser_connection_ldap_get_base_dn (eview->priv->bcnc);
+ array = g_array_new (TRUE, FALSE, sizeof (gchar*));
+ make_dn_hierarchy (basedn, dn, array);
+ if (array->len > 0) {
+ eview->priv->to_show = array;
+ go_to_row (eview, NULL);
+ }
+ else
+ g_array_free (array, TRUE);
+ }
+
+ return (GtkWidget*) eview;
+}
+
+
+static gboolean
+make_dn_hierarchy (const gchar *basedn, const gchar *dn, GArray *in)
+{
+ if (basedn && !strcmp (basedn, dn))
+ return TRUE;
+
+ gchar **split;
+ gchar *tmp;
+ tmp = g_strdup (dn);
+ g_array_prepend_val (in, tmp);
+ split = gda_ldap_dn_split (dn, FALSE);
+ if (split) {
+ gboolean retval = TRUE;
+ if (split [0] && split [1])
+ retval = make_dn_hierarchy (basedn, split [1], in);
+ g_strfreev (split);
+ return retval;
+ }
+ else
+ return FALSE;
+}
+
+static void
+selection_changed_cb (GtkTreeSelection *sel, HierarchyView *eview)
+{
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+ if (gtk_tree_selection_get_selected (sel, &model, &iter)) {
+ GdaTreeNode *node;
+ const GValue *cvalue;
+ node = gdaui_tree_store_get_node (GDAUI_TREE_STORE (model), &iter);
+ g_assert (node);
+ cvalue = gda_tree_node_get_node_attribute (node, "dn");
+ g_assert (cvalue);
+
+ g_free (eview->priv->current_dn);
+ eview->priv->current_dn = g_value_dup_string (cvalue);
+ g_free (eview->priv->current_cn);
+ eview->priv->current_cn = NULL;
+ cvalue = gda_tree_node_get_node_attribute (node, "cn");
+ if (cvalue)
+ eview->priv->current_cn = g_value_dup_string (cvalue);
+ }
+}
+
+typedef struct {
+ GtkTreeView *tview;
+ GdauiTreeStore *store;
+ GdaTree *tree;
+ GdaTreeNode *node;
+} IdleData;
+
+static void
+idle_data_free (IdleData *data)
+{
+ g_object_unref (data->tview);
+ g_object_unref (data->store);
+ g_object_unref (data->tree);
+ g_object_unref (data->node);
+ g_free (data);
+}
+
+static gboolean ldap_update_tree_part (IdleData *data);
+static gboolean
+test_expand_row_cb (GtkTreeView *tree_view, GtkTreeIter *iter,
+ G_GNUC_UNUSED GtkTreePath *path, HierarchyView *eview)
+{
+ GdaTreeNode *node;
+ GtkTreeModel *store;
+#ifdef DEBUG_GOTO
+ g_print ("%s (path => %s)\n", __FUNCTION__, gtk_tree_path_to_string (path));
+#endif
+ store = gtk_tree_view_get_model (tree_view);
+ node = gdaui_tree_store_get_node (GDAUI_TREE_STORE (store), iter);
+ if (!node || gda_tree_node_get_child_index (node, 0))
+ return FALSE;
+
+ const GValue *cv;
+ cv = gda_tree_node_get_node_attribute (node,
+ GDA_ATTRIBUTE_TREE_NODE_UNKNOWN_CHILDREN);
+ if (cv && (G_VALUE_TYPE (cv) == G_TYPE_BOOLEAN) &&
+ g_value_get_boolean (cv)) {
+ IdleData *data;
+ data = g_new (IdleData, 1);
+ data->tview = g_object_ref (G_OBJECT (tree_view));
+ data->store = g_object_ref (G_OBJECT (store));
+ data->tree = g_object_ref (G_OBJECT (eview->priv->ldap_tree));
+ data->node = g_object_ref (G_OBJECT (node));
+
+ g_idle_add_full (G_PRIORITY_HIGH_IDLE, (GSourceFunc) ldap_update_tree_part,
+ data, (GDestroyNotify) idle_data_free);
+ }
+ return TRUE;
+}
+
+static gboolean
+ldap_update_tree_part (IdleData *data)
+{
+ gda_tree_update_children (data->tree, data->node, NULL);
+
+ GtkTreeIter iter;
+ if (gdaui_tree_store_get_iter (data->store, &iter, data->node)) {
+ if (gda_tree_node_get_child_index (data->node, 0)) {
+ /* actually expand the row */
+ GtkTreePath *path;
+ path = gtk_tree_model_get_path (GTK_TREE_MODEL (data->store), &iter);
+ g_signal_handlers_block_by_func (data->tview,
+ G_CALLBACK (test_expand_row_cb), data->tree);
+ gtk_tree_view_expand_row (data->tview, path, FALSE);
+ gtk_tree_view_scroll_to_cell (data->tview,
+ path, NULL, TRUE, 0.5, 0.5);
+ g_signal_handlers_unblock_by_func (data->tview,
+ G_CALLBACK (test_expand_row_cb), data->tree);
+ gtk_tree_path_free (path);
+ }
+ if (HIERARCHY_VIEW (data->tview)->priv->to_show) {
+ GtkTreePath *path;
+ path = gtk_tree_model_get_path (GTK_TREE_MODEL (data->store), &iter);
+ go_to_row (HIERARCHY_VIEW (data->tview), path);
+ gtk_tree_path_free (path);
+ }
+ }
+
+ return FALSE; /* => remove IDLE call */
+}
+
+static void
+go_to_row (HierarchyView *eview, GtkTreePath *path)
+{
+ GtkTreePath *lpath = NULL;
+ gchar *tofind = NULL;
+ if (path)
+ lpath = gtk_tree_path_copy (path);
+#ifdef DEBUG_GOTO
+ g_print ("%s (starting from path=>%s)\n", __FUNCTION__, path ? gtk_tree_path_to_string (path) : "null");
+#endif
+ while (1) {
+ if (!eview->priv->to_show)
+ goto out;
+ if (eview->priv->to_show->len == 0) {
+ g_array_free (eview->priv->to_show, TRUE);
+ eview->priv->to_show = NULL;
+ goto out;
+ }
+
+ g_free (tofind);
+ tofind = g_array_index (eview->priv->to_show, gchar*, 0);
+ g_array_remove_index (eview->priv->to_show, 0);
+
+#ifdef DEBUG_GOTO
+ g_print ("tofind: [%s] starting from [%s]\n", tofind, lpath ? gtk_tree_path_to_string (lpath) : "null");
+#endif
+ GtkTreeModel *model;
+ GtkTreeIter iter, parent;
+ gboolean found = FALSE;
+ model = GTK_TREE_MODEL (eview->priv->ldap_store);
+ if ((lpath && gtk_tree_model_get_iter (model, &parent, lpath) &&
+ gtk_tree_model_iter_children (model, &iter, &parent)) ||
+ (!lpath && gtk_tree_model_get_iter_first (model, &iter))) {
+ gboolean init = TRUE;
+ for (; init || (!init && gtk_tree_model_iter_next (model, &iter)); init = FALSE) {
+ gchar *dn;
+ gtk_tree_model_get (model, &iter, COLUMN_DN, &dn, -1);
+ if (dn) {
+ if (!strcmp (dn, tofind)) {
+ found = TRUE;
+ g_free (dn);
+ break;
+ }
+ g_free (dn);
+ }
+ }
+ }
+ if (!found) {
+ /* perform the same search but case insensitive */
+ if ((lpath && gtk_tree_model_get_iter (model, &parent, lpath) &&
+ gtk_tree_model_iter_children (model, &iter, &parent)) ||
+ (!lpath && gtk_tree_model_get_iter_first (model, &iter))) {
+ gboolean init = TRUE;
+ for (; init || (!init && gtk_tree_model_iter_next (model, &iter)); init = FALSE) {
+ gchar *dn;
+ gtk_tree_model_get (model, &iter, COLUMN_DN, &dn, -1);
+ if (dn) {
+ if (!g_ascii_strcasecmp (dn, tofind)) {
+ found = TRUE;
+ break;
+ }
+ g_free (dn);
+ }
+ }
+ }
+ }
+ if (lpath) {
+ gtk_tree_path_free (lpath);
+ lpath = NULL;
+ }
+
+ if (found) {
+ /* iter is pointing to the requested DN */
+ lpath = gtk_tree_model_get_path (model, &iter);
+ if (eview->priv->to_show->len == 0) {
+ /* no more after => set selection */
+ GtkTreeSelection *sel;
+ sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (eview));
+ gtk_tree_selection_select_path (sel, lpath);
+ gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (eview),
+ lpath, NULL, TRUE, 0.5, 0.5);
+#ifdef DEBUG_GOTO
+ g_print ("SELECTED %s\n", gtk_tree_path_to_string (lpath));
+#endif
+ goto out;
+ }
+ else {
+ /* more to find */
+ if (! gtk_tree_view_row_expanded (GTK_TREE_VIEW (eview), lpath)) {
+ GdaTreeNode *node;
+ node = gdaui_tree_store_get_node (eview->priv->ldap_store, &iter);
+ if (node && gda_tree_node_get_child_index (node, 0))
+ gtk_tree_view_expand_row (GTK_TREE_VIEW (eview), lpath, FALSE);
+ else {
+
+#ifdef DEBUG_GOTO
+ g_print (">>REQUEST ROW EXP for path %s\n", gtk_tree_path_to_string (lpath));
+#endif
+ gtk_tree_view_expand_row (GTK_TREE_VIEW (eview), lpath, FALSE);
+
+#ifdef DEBUG_GOTO
+ g_print ("<<REQUEST ROW EXP for path %s\n", gtk_tree_path_to_string (lpath));
+#endif
+ goto out;
+ }
+ }
+ }
+ }
+ else {
+ /* DN not found! */
+ browser_show_message (GTK_WINDOW (gtk_widget_get_toplevel ((GtkWidget*) eview)),
+ _("Could not find LDAP entry whith DN '%s'"), tofind);
+ goto out;
+ }
+ }
+ out:
+ g_free (tofind);
+ if (lpath)
+ gtk_tree_path_free (lpath);
+}
+
+
+/**
+ * hierarchy_view_get_current_dn:
+ */
+const gchar *
+hierarchy_view_get_current_dn (HierarchyView *eview, const gchar **out_current_cn)
+{
+ g_return_val_if_fail (IS_HIERARCHY_VIEW (eview), NULL);
+ if (out_current_cn)
+ *out_current_cn = eview->priv->current_cn;
+ return eview->priv->current_dn;
+}
+
+/**
+ * hierarchy_view_set_current_dn:
+ */
+void
+hierarchy_view_set_current_dn (HierarchyView *hierarchy_view, const gchar *dn)
+{
+ const gchar *basedn;
+ GArray *array;
+
+ g_return_if_fail (IS_HIERARCHY_VIEW (hierarchy_view));
+ g_return_if_fail (dn && *dn);
+
+ if (hierarchy_view->priv->to_show) {
+ guint i;
+ for (i = 0; i < hierarchy_view->priv->to_show->len; i++) {
+ gchar *tmp;
+ tmp = (gchar*) g_array_index (hierarchy_view->priv->to_show, gchar*, i);
+ g_free (tmp);
+ }
+ g_array_free (hierarchy_view->priv->to_show, TRUE);
+ hierarchy_view->priv->to_show = NULL;
+ }
+
+ basedn = browser_connection_ldap_get_base_dn (hierarchy_view->priv->bcnc);
+ array = g_array_new (TRUE, FALSE, sizeof (gchar*));
+ make_dn_hierarchy (basedn, dn, array);
+ if (array->len > 0) {
+ hierarchy_view->priv->to_show = array;
+ go_to_row (hierarchy_view, NULL);
+ }
+ else
+ g_array_free (array, TRUE);
+}
diff --git a/tools/browser/ldap-browser/hierarchy-view.h b/tools/browser/ldap-browser/hierarchy-view.h
new file mode 100644
index 0000000..8ed357b
--- /dev/null
+++ b/tools/browser/ldap-browser/hierarchy-view.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2011 The GNOME Foundation
+ *
+ * AUTHORS:
+ * Vivien Malerba <malerba gnome-db org>
+ *
+ * This Program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __HIERARCHY_VIEW_H__
+#define __HIERARCHY_VIEW_H__
+
+#include <gtk/gtk.h>
+#include "../browser-connection.h"
+
+G_BEGIN_DECLS
+
+#define HIERARCHY_VIEW_TYPE (hierarchy_view_get_type())
+#define HIERARCHY_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, HIERARCHY_VIEW_TYPE, HierarchyView))
+#define HIERARCHY_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, HIERARCHY_VIEW_TYPE, HierarchyViewClass))
+#define IS_HIERARCHY_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, HIERARCHY_VIEW_TYPE))
+#define IS_HIERARCHY_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), HIERARCHY_VIEW_TYPE))
+
+typedef struct _HierarchyView HierarchyView;
+typedef struct _HierarchyViewClass HierarchyViewClass;
+typedef struct _HierarchyViewPrivate HierarchyViewPrivate;
+
+struct _HierarchyView {
+ GtkTreeView parent;
+ HierarchyViewPrivate *priv;
+};
+
+struct _HierarchyViewClass {
+ GtkTreeViewClass parent_class;
+};
+
+GType hierarchy_view_get_type (void) G_GNUC_CONST;
+
+GtkWidget *hierarchy_view_new (BrowserConnection *bcnc, const gchar *dn);
+const gchar *hierarchy_view_get_current_dn (HierarchyView *hierarchy_view, const gchar **out_current_cn);
+void hierarchy_view_set_current_dn (HierarchyView *hierarchy_view, const gchar *dn);
+
+G_END_DECLS
+
+#endif
diff --git a/tools/browser/ldap-browser/ldap-browser-perspective.c b/tools/browser/ldap-browser/ldap-browser-perspective.c
new file mode 100644
index 0000000..3de8662
--- /dev/null
+++ b/tools/browser/ldap-browser/ldap-browser-perspective.c
@@ -0,0 +1,531 @@
+/*
+ * Copyright (C) 2011 Vivien Malerba
+ *
+ * This Program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+#include <glib/gi18n-lib.h>
+#include "ldap-browser-perspective.h"
+#include "../browser-window.h"
+#include "ldap-entries-page.h"
+#include "ldap-classes-page.h"
+#include "ldap-search-page.h"
+#include "../support.h"
+#include "../browser-page.h"
+#include "../browser-favorites.h"
+#include "ldap-favorite-selector.h"
+#include "../browser-stock-icons.h"
+
+/*
+ * Main static functions
+ */
+static void ldap_browser_perspective_class_init (LdapBrowserPerspectiveClass *klass);
+static void ldap_browser_perspective_init (LdapBrowserPerspective *stmt);
+static void ldap_browser_perspective_dispose (GObject *object);
+
+/* BrowserPerspective interface */
+static void ldap_browser_perspective_perspective_init (BrowserPerspectiveIface *iface);
+static BrowserWindow *ldap_browser_perspective_get_window (BrowserPerspective *perspective);
+static GtkActionGroup *ldap_browser_perspective_get_actions_group (BrowserPerspective *perspective);
+static const gchar *ldap_browser_perspective_get_actions_ui (BrowserPerspective *perspective);
+static void ldap_browser_perspective_page_tab_label_change (BrowserPerspective *perspective, BrowserPage *page);
+static void ldap_browser_perspective_get_current_customization (BrowserPerspective *perspective,
+ GtkActionGroup **out_agroup,
+ const gchar **out_ui);
+
+/* get a pointer to the parents to be able to call their destructor */
+static GObjectClass *parent_class = NULL;
+
+struct _LdapBrowserPerspectivePrivate {
+ GtkWidget *notebook;
+ GtkWidget *favorites;
+ gboolean favorites_shown;
+ BrowserWindow *bwin;
+};
+
+GType
+ldap_browser_perspective_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0)) {
+ static GStaticMutex registering = G_STATIC_MUTEX_INIT;
+ static const GTypeInfo info = {
+ sizeof (LdapBrowserPerspectiveClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) ldap_browser_perspective_class_init,
+ NULL,
+ NULL,
+ sizeof (LdapBrowserPerspective),
+ 0,
+ (GInstanceInitFunc) ldap_browser_perspective_init,
+ 0
+ };
+
+ static GInterfaceInfo perspective_info = {
+ (GInterfaceInitFunc) ldap_browser_perspective_perspective_init,
+ NULL,
+ NULL
+ };
+
+ g_static_mutex_lock (®istering);
+ if (type == 0) {
+ type = g_type_register_static (GTK_TYPE_VBOX, "LdapBrowserPerspective", &info, 0);
+ g_type_add_interface_static (type, BROWSER_PERSPECTIVE_TYPE, &perspective_info);
+ }
+ g_static_mutex_unlock (®istering);
+ }
+ return type;
+}
+
+static void
+ldap_browser_perspective_class_init (LdapBrowserPerspectiveClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ parent_class = g_type_class_peek_parent (klass);
+
+ object_class->dispose = ldap_browser_perspective_dispose;
+}
+
+static void
+ldap_browser_perspective_perspective_init (BrowserPerspectiveIface *iface)
+{
+ iface->i_get_window = ldap_browser_perspective_get_window;
+ iface->i_get_actions_group = ldap_browser_perspective_get_actions_group;
+ iface->i_get_actions_ui = ldap_browser_perspective_get_actions_ui;
+ iface->i_page_tab_label_change = ldap_browser_perspective_page_tab_label_change;
+ iface->i_get_current_customization = ldap_browser_perspective_get_current_customization;
+}
+
+
+static void
+ldap_browser_perspective_init (LdapBrowserPerspective *perspective)
+{
+ perspective->priv = g_new0 (LdapBrowserPerspectivePrivate, 1);
+ perspective->priv->favorites_shown = TRUE;
+}
+
+static void
+close_button_clicked_cb (G_GNUC_UNUSED GtkWidget *wid, GtkWidget *page_widget)
+{
+ gtk_widget_destroy (page_widget);
+}
+
+static void fav_selection_changed_cb (GtkWidget *widget, gint fav_id, BrowserFavoritesType fav_type,
+ const gchar *selection, LdapBrowserPerspective *bpers);
+/**
+ * ldap_browser_perspective_new
+ *
+ * Creates new #BrowserPerspective widget which
+ */
+BrowserPerspective *
+ldap_browser_perspective_new (BrowserWindow *bwin)
+{
+ BrowserConnection *bcnc;
+ BrowserPerspective *bpers;
+ LdapBrowserPerspective *perspective;
+
+ bpers = (BrowserPerspective*) g_object_new (TYPE_LDAP_BROWSER_PERSPECTIVE, NULL);
+ perspective = (LdapBrowserPerspective*) bpers;
+
+ perspective->priv->bwin = bwin;
+
+ /* contents */
+ GtkWidget *paned, *wid, *nb, *button, *tlabel;
+ bcnc = browser_window_get_connection (bwin);
+ paned = gtk_hpaned_new ();
+ wid = ldap_favorite_selector_new (bcnc);
+ g_signal_connect (wid, "selection-changed",
+ G_CALLBACK (fav_selection_changed_cb), bpers);
+ gtk_paned_add1 (GTK_PANED (paned), wid);
+ gtk_paned_set_position (GTK_PANED (paned), DEFAULT_FAVORITES_SIZE);
+ perspective->priv->favorites = wid;
+
+ nb = gtk_notebook_new ();
+ perspective->priv->notebook = nb;
+ gtk_paned_add2 (GTK_PANED (paned), nb);
+ gtk_notebook_set_scrollable (GTK_NOTEBOOK (nb), TRUE);
+ gtk_notebook_popup_enable (GTK_NOTEBOOK (nb));
+
+ wid = ldap_entries_page_new (bcnc, NULL);
+ tlabel = browser_page_get_tab_label (BROWSER_PAGE (wid), &button);
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (close_button_clicked_cb), wid);
+
+ gtk_notebook_append_page (GTK_NOTEBOOK (nb), wid, tlabel);
+ gtk_notebook_set_tab_reorderable (GTK_NOTEBOOK (nb), wid, TRUE);
+ gtk_notebook_set_group_name (GTK_NOTEBOOK (nb), "ldap-browser");
+
+ gtk_box_pack_start (GTK_BOX (bpers), paned, TRUE, TRUE, 0);
+ gtk_widget_show_all (paned);
+
+ if (!perspective->priv->favorites_shown)
+ gtk_widget_hide (perspective->priv->favorites);
+
+ browser_perspective_declare_notebook (bpers, GTK_NOTEBOOK (perspective->priv->notebook));
+
+ return bpers;
+}
+
+static void
+fav_selection_changed_cb (G_GNUC_UNUSED GtkWidget *widget, G_GNUC_UNUSED gint fav_id,
+ BrowserFavoritesType fav_type,
+ const gchar *selection, LdapBrowserPerspective *bpers)
+{
+ if (fav_type == BROWSER_FAVORITES_LDAP_DN) {
+ ldap_browser_perspective_display_ldap_entry (bpers, selection);
+ }
+ if (fav_type == BROWSER_FAVORITES_LDAP_CLASS) {
+ ldap_browser_perspective_display_ldap_class (bpers, selection);
+ }
+#ifdef GDA_DEBUG_NO
+ g_print ("Reacted to selection fav_id=>%d type=>%u, contents=>%s\n", fav_id, fav_type, selection);
+#endif
+}
+
+static void
+ldap_browser_perspective_dispose (GObject *object)
+{
+ LdapBrowserPerspective *perspective;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (IS_LDAP_BROWSER_PERSPECTIVE (object));
+
+ perspective = LDAP_BROWSER_PERSPECTIVE (object);
+ if (perspective->priv) {
+ browser_perspective_declare_notebook ((BrowserPerspective*) perspective, NULL);
+ g_free (perspective->priv);
+ perspective->priv = NULL;
+ }
+
+ /* parent class */
+ parent_class->dispose (object);
+}
+
+static void
+favorites_toggle_cb (GtkToggleAction *action, BrowserPerspective *bpers)
+{
+ LdapBrowserPerspective *perspective;
+ perspective = LDAP_BROWSER_PERSPECTIVE (bpers);
+ perspective->priv->favorites_shown = gtk_toggle_action_get_active (action);
+ if (perspective->priv->favorites_shown)
+ gtk_widget_show (perspective->priv->favorites);
+ else
+ gtk_widget_hide (perspective->priv->favorites);
+}
+
+static void
+ldab_ldap_entries_page_add_cb (G_GNUC_UNUSED GtkAction *action, BrowserPerspective *bpers)
+{
+ GtkWidget *page, *tlabel, *button;
+ LdapBrowserPerspective *perspective;
+ BrowserConnection *bcnc;
+ gint i;
+
+ perspective = LDAP_BROWSER_PERSPECTIVE (bpers);
+ bcnc = browser_window_get_connection (perspective->priv->bwin);
+
+ page = ldap_entries_page_new (bcnc, NULL);
+ gtk_widget_show (page);
+ tlabel = browser_page_get_tab_label (BROWSER_PAGE (page), &button);
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (close_button_clicked_cb), page);
+
+ i = gtk_notebook_append_page (GTK_NOTEBOOK (perspective->priv->notebook), page, tlabel);
+ gtk_notebook_set_current_page (GTK_NOTEBOOK (perspective->priv->notebook), i);
+
+ gtk_notebook_set_tab_reorderable (GTK_NOTEBOOK (perspective->priv->notebook), page, TRUE);
+ gtk_notebook_set_tab_detachable (GTK_NOTEBOOK (perspective->priv->notebook), page,
+ TRUE);
+
+ tlabel = browser_page_get_tab_label (BROWSER_PAGE (page), NULL);
+ gtk_notebook_set_menu_label (GTK_NOTEBOOK (perspective->priv->notebook), page, tlabel);
+
+ gtk_widget_grab_focus (page);
+}
+
+static void
+ldab_ldap_classes_page_add_cb (G_GNUC_UNUSED GtkAction *action, BrowserPerspective *bpers)
+{
+ GtkWidget *page, *tlabel, *button;
+ LdapBrowserPerspective *perspective;
+ BrowserConnection *bcnc;
+ gint i;
+
+ perspective = LDAP_BROWSER_PERSPECTIVE (bpers);
+ bcnc = browser_window_get_connection (perspective->priv->bwin);
+
+ page = ldap_classes_page_new (bcnc, NULL);
+ gtk_widget_show (page);
+ tlabel = browser_page_get_tab_label (BROWSER_PAGE (page), &button);
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (close_button_clicked_cb), page);
+
+ i = gtk_notebook_append_page (GTK_NOTEBOOK (perspective->priv->notebook), page, tlabel);
+ gtk_notebook_set_current_page (GTK_NOTEBOOK (perspective->priv->notebook), i);
+
+ gtk_notebook_set_tab_reorderable (GTK_NOTEBOOK (perspective->priv->notebook), page, TRUE);
+ gtk_notebook_set_tab_detachable (GTK_NOTEBOOK (perspective->priv->notebook), page,
+ TRUE);
+
+ tlabel = browser_page_get_tab_label (BROWSER_PAGE (page), NULL);
+ gtk_notebook_set_menu_label (GTK_NOTEBOOK (perspective->priv->notebook), page, tlabel);
+
+ gtk_widget_grab_focus (page);
+}
+
+static void
+ldab_search_add_cb (G_GNUC_UNUSED GtkAction *action, BrowserPerspective *bpers)
+{
+ GtkWidget *page, *tlabel, *button;
+ LdapBrowserPerspective *perspective;
+ BrowserConnection *bcnc;
+ gint i;
+
+ perspective = LDAP_BROWSER_PERSPECTIVE (bpers);
+ bcnc = browser_window_get_connection (perspective->priv->bwin);
+
+ i = gtk_notebook_get_current_page (GTK_NOTEBOOK (perspective->priv->notebook));
+ GtkWidget *child;
+ const gchar *dn = NULL;
+ child = gtk_notebook_get_nth_page (GTK_NOTEBOOK (perspective->priv->notebook), i);
+ if (IS_LDAP_ENTRIES_PAGE (child))
+ dn = ldap_entries_page_get_current_dn (LDAP_ENTRIES_PAGE (child));
+
+ page = ldap_search_page_new (bcnc, dn);
+ gtk_widget_show (page);
+ tlabel = browser_page_get_tab_label (BROWSER_PAGE (page), &button);
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (close_button_clicked_cb), page);
+
+ i = gtk_notebook_append_page (GTK_NOTEBOOK (perspective->priv->notebook), page, tlabel);
+ gtk_notebook_set_current_page (GTK_NOTEBOOK (perspective->priv->notebook), i);
+
+ gtk_notebook_set_tab_reorderable (GTK_NOTEBOOK (perspective->priv->notebook), page, TRUE);
+ gtk_notebook_set_tab_detachable (GTK_NOTEBOOK (perspective->priv->notebook), page,
+ TRUE);
+
+ tlabel = browser_page_get_tab_label (BROWSER_PAGE (page), NULL);
+ gtk_notebook_set_menu_label (GTK_NOTEBOOK (perspective->priv->notebook), page, tlabel);
+
+ gtk_widget_grab_focus (page);
+}
+
+static const GtkToggleActionEntry ui_toggle_actions [] =
+ {
+ { "LdapBrowserFavoritesShow", NULL, N_("_Show favorites"), "F9", N_("Show or hide favorites"), G_CALLBACK (favorites_toggle_cb), FALSE }
+ };
+
+static GtkActionEntry ui_actions[] = {
+ { "LDAP", NULL, N_("_LDAP"), NULL, N_("LDAP"), NULL },
+ { "LdapLdapEntriesPageNew", BROWSER_STOCK_LDAP_ENTRIES, N_("_New LDAP entries browser"), "<control>T", N_("Open a new LDAP entries browser"),
+ G_CALLBACK (ldab_ldap_entries_page_add_cb)},
+ { "LdapLdapClassesPageNew", NULL, N_("_New LDAP classes browser"), "<control>C", N_("Open a new LDAP classes browser"),
+ G_CALLBACK (ldab_ldap_classes_page_add_cb)},
+ { "LdapSearchNew", GTK_STOCK_FIND, N_("_New LDAP search"), "<control>F", N_("Open a new LDAP search form"),
+ G_CALLBACK (ldab_search_add_cb)},
+};
+
+static const gchar *ui_actions_info =
+ "<ui>"
+ " <menubar name='MenuBar'>"
+ " <menu name='Display' action='Display'>"
+ " <menuitem name='LdapBrowserFavoritesShow' action='LdapBrowserFavoritesShow'/>"
+ " </menu>"
+ " <placeholder name='MenuExtension'>"
+ " <menu name='LDAP' action='LDAP'>"
+ " <menuitem name='LdapSearchNew' action= 'LdapSearchNew'/>"
+ " <menuitem name='LdapLdapEntriesPageNew' action= 'LdapLdapEntriesPageNew'/>"
+ " <menuitem name='LdapLdapClassesPageNew' action= 'LdapLdapClassesPageNew'/>"
+ " </menu>"
+ " </placeholder>"
+ " </menubar>"
+ " <toolbar name='ToolBar'>"
+ " <separator/>"
+ " <toolitem action='LdapSearchNew'/>"
+ " <toolitem action='LdapLdapEntriesPageNew'/>"
+ " </toolbar>"
+ "</ui>";
+
+static GtkActionGroup *
+ldap_browser_perspective_get_actions_group (BrowserPerspective *bpers)
+{
+ GtkActionGroup *agroup;
+ agroup = gtk_action_group_new ("LdapBrowserActions");
+ gtk_action_group_set_translation_domain (agroup, GETTEXT_PACKAGE);
+
+ gtk_action_group_add_actions (agroup, ui_actions, G_N_ELEMENTS (ui_actions), bpers);
+ gtk_action_group_add_toggle_actions (agroup, ui_toggle_actions, G_N_ELEMENTS (ui_toggle_actions),
+ bpers);
+ GtkAction *action;
+ action = gtk_action_group_get_action (agroup, "LdapBrowserFavoritesShow");
+ gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
+ LDAP_BROWSER_PERSPECTIVE (bpers)->priv->favorites_shown);
+
+ return agroup;
+}
+
+static const gchar *
+ldap_browser_perspective_get_actions_ui (G_GNUC_UNUSED BrowserPerspective *bpers)
+{
+ return ui_actions_info;
+}
+
+static void
+ldap_browser_perspective_page_tab_label_change (BrowserPerspective *perspective, BrowserPage *page)
+{
+ LdapBrowserPerspective *bpers;
+ GtkWidget *tab_label;
+ GtkWidget *close_btn;
+
+ bpers = LDAP_BROWSER_PERSPECTIVE (perspective);
+ tab_label = browser_page_get_tab_label (page, &close_btn);
+ if (tab_label) {
+ gtk_notebook_set_tab_label (GTK_NOTEBOOK (bpers->priv->notebook),
+ GTK_WIDGET (page), tab_label);
+ g_signal_connect (close_btn, "clicked",
+ G_CALLBACK (close_button_clicked_cb), page);
+
+ tab_label = browser_page_get_tab_label (page, NULL);
+ gtk_notebook_set_menu_label (GTK_NOTEBOOK (bpers->priv->notebook),
+ GTK_WIDGET (page), tab_label);
+ }
+}
+
+/**
+ * ldap_browser_perspective_display_ldap_entry
+ *
+ * Display (and create if necessary) a new page for the LDAP entry's properties
+ */
+void
+ldap_browser_perspective_display_ldap_entry (LdapBrowserPerspective *bpers, const gchar *dn)
+{
+ g_return_if_fail (IS_LDAP_BROWSER_PERSPECTIVE (bpers));
+
+ gint ntabs, i;
+ ntabs = gtk_notebook_get_n_pages (GTK_NOTEBOOK (bpers->priv->notebook));
+ i = gtk_notebook_get_current_page (GTK_NOTEBOOK (bpers->priv->notebook));
+ for (; i < ntabs; i++) {
+ GtkWidget *child;
+ child = gtk_notebook_get_nth_page (GTK_NOTEBOOK (bpers->priv->notebook), i);
+ if (IS_LDAP_ENTRIES_PAGE (child)) {
+ ldap_entries_page_set_current_dn (LDAP_ENTRIES_PAGE (child), dn);
+ gtk_notebook_set_current_page (GTK_NOTEBOOK (bpers->priv->notebook), i);
+ return;
+ }
+ }
+
+ /* open a new page */
+ GtkWidget *ti;
+ ti = ldap_entries_page_new (browser_window_get_connection (bpers->priv->bwin), dn);
+ if (ti) {
+ GtkWidget *close_btn;
+ GtkWidget *tab_label;
+ gint i;
+
+ tab_label = browser_page_get_tab_label (BROWSER_PAGE (ti), &close_btn);
+ i = gtk_notebook_append_page (GTK_NOTEBOOK (bpers->priv->notebook), ti, tab_label);
+ g_signal_connect (close_btn, "clicked",
+ G_CALLBACK (close_button_clicked_cb), ti);
+
+ gtk_widget_show (ti);
+
+ tab_label = browser_page_get_tab_label (BROWSER_PAGE (ti), NULL);
+ gtk_notebook_set_menu_label (GTK_NOTEBOOK (bpers->priv->notebook), ti, tab_label);
+
+ gtk_notebook_set_current_page (GTK_NOTEBOOK (bpers->priv->notebook), i);
+ gtk_notebook_set_tab_reorderable (GTK_NOTEBOOK (bpers->priv->notebook), ti,
+ TRUE);
+ gtk_notebook_set_tab_detachable (GTK_NOTEBOOK (bpers->priv->notebook), ti,
+ TRUE);
+ }
+}
+
+/**
+ * ldap_browser_perspective_display_ldap_class
+ *
+ * Display (and create if necessary) a new page for the LDAP class's properties
+ */
+void
+ldap_browser_perspective_display_ldap_class (LdapBrowserPerspective *bpers, const gchar *classname)
+{
+ g_return_if_fail (IS_LDAP_BROWSER_PERSPECTIVE (bpers));
+
+ gint ntabs, i;
+ ntabs = gtk_notebook_get_n_pages (GTK_NOTEBOOK (bpers->priv->notebook));
+ i = gtk_notebook_get_current_page (GTK_NOTEBOOK (bpers->priv->notebook));
+ for (; i < ntabs; i++) {
+ GtkWidget *child;
+ child = gtk_notebook_get_nth_page (GTK_NOTEBOOK (bpers->priv->notebook), i);
+ if (IS_LDAP_CLASSES_PAGE (child)) {
+ ldap_classes_page_set_current_class (LDAP_CLASSES_PAGE (child), classname);
+ gtk_notebook_set_current_page (GTK_NOTEBOOK (bpers->priv->notebook), i);
+ return;
+ }
+ }
+
+ /* open a new page */
+ GtkWidget *ti;
+ ti = ldap_classes_page_new (browser_window_get_connection (bpers->priv->bwin), classname);
+ if (ti) {
+ GtkWidget *close_btn;
+ GtkWidget *tab_label;
+ gint i;
+
+ tab_label = browser_page_get_tab_label (BROWSER_PAGE (ti), &close_btn);
+ i = gtk_notebook_append_page (GTK_NOTEBOOK (bpers->priv->notebook), ti, tab_label);
+ g_signal_connect (close_btn, "clicked",
+ G_CALLBACK (close_button_clicked_cb), ti);
+
+ gtk_widget_show (ti);
+
+ tab_label = browser_page_get_tab_label (BROWSER_PAGE (ti), NULL);
+ gtk_notebook_set_menu_label (GTK_NOTEBOOK (bpers->priv->notebook), ti, tab_label);
+
+ gtk_notebook_set_current_page (GTK_NOTEBOOK (bpers->priv->notebook), i);
+ gtk_notebook_set_tab_reorderable (GTK_NOTEBOOK (bpers->priv->notebook), ti,
+ TRUE);
+ gtk_notebook_set_tab_detachable (GTK_NOTEBOOK (bpers->priv->notebook), ti,
+ TRUE);
+ }
+}
+
+static void
+ldap_browser_perspective_get_current_customization (BrowserPerspective *perspective,
+ GtkActionGroup **out_agroup,
+ const gchar **out_ui)
+{
+ LdapBrowserPerspective *bpers;
+ GtkWidget *page_contents;
+
+ bpers = LDAP_BROWSER_PERSPECTIVE (perspective);
+ page_contents = gtk_notebook_get_nth_page (GTK_NOTEBOOK (bpers->priv->notebook),
+ gtk_notebook_get_current_page (GTK_NOTEBOOK (bpers->priv->notebook)));
+ if (IS_BROWSER_PAGE (page_contents)) {
+ *out_agroup = browser_page_get_actions_group (BROWSER_PAGE (page_contents));
+ *out_ui = browser_page_get_actions_ui (BROWSER_PAGE (page_contents));
+ }
+}
+
+static BrowserWindow *
+ldap_browser_perspective_get_window (BrowserPerspective *perspective)
+{
+ LdapBrowserPerspective *bpers;
+ bpers = LDAP_BROWSER_PERSPECTIVE (perspective);
+ return bpers->priv->bwin;
+}
diff --git a/tools/browser/ldap-browser/ldap-browser-perspective.h b/tools/browser/ldap-browser/ldap-browser-perspective.h
new file mode 100644
index 0000000..5d86097
--- /dev/null
+++ b/tools/browser/ldap-browser/ldap-browser-perspective.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2011 Vivien Malerba
+ *
+ * This Program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef __LDAP_BROWSER_PERSPECTIVE_H_
+#define __LDAP_BROWSER_PERSPECTIVE_H_
+
+#include <glib-object.h>
+#include "../browser-perspective.h"
+
+G_BEGIN_DECLS
+
+#define TYPE_LDAP_BROWSER_PERSPECTIVE (ldap_browser_perspective_get_type())
+#define LDAP_BROWSER_PERSPECTIVE(obj) G_TYPE_CHECK_INSTANCE_CAST (obj, ldap_browser_perspective_get_type(), LdapBrowserPerspective)
+#define LDAP_BROWSER_PERSPECTIVE_CLASS(klass) G_TYPE_CHECK_CLASS_CAST (klass, ldap_browser_perspective_get_type (), LdapBrowserPerspectiveClass)
+#define IS_LDAP_BROWSER_PERSPECTIVE(obj) G_TYPE_CHECK_INSTANCE_TYPE (obj, ldap_browser_perspective_get_type ())
+
+typedef struct _LdapBrowserPerspective LdapBrowserPerspective;
+typedef struct _LdapBrowserPerspectiveClass LdapBrowserPerspectiveClass;
+typedef struct _LdapBrowserPerspectivePrivate LdapBrowserPerspectivePrivate;
+
+/* struct for the object's data */
+struct _LdapBrowserPerspective
+{
+ GtkVBox object;
+ LdapBrowserPerspectivePrivate *priv;
+};
+
+/* struct for the object's class */
+struct _LdapBrowserPerspectiveClass
+{
+ GtkVBoxClass parent_class;
+};
+
+/**
+ * SECTION:ldap-browser-perspective
+ * @short_description: Perspective to analyse the database's ldap
+ * @title: Ldap Browser perspective
+ * @stability: Stable
+ * @see_also:
+ */
+
+GType ldap_browser_perspective_get_type (void) G_GNUC_CONST;
+BrowserPerspective *ldap_browser_perspective_new (BrowserWindow *bwin);
+
+void ldap_browser_perspective_display_ldap_entry (LdapBrowserPerspective *bpers,
+ const gchar *dn);
+void ldap_browser_perspective_display_ldap_class (LdapBrowserPerspective *bpers,
+ const gchar *classname);
+
+G_END_DECLS
+
+#endif
diff --git a/tools/browser/ldap-browser/ldap-classes-page.c b/tools/browser/ldap-browser/ldap-classes-page.c
new file mode 100644
index 0000000..4b677cf
--- /dev/null
+++ b/tools/browser/ldap-browser/ldap-classes-page.c
@@ -0,0 +1,627 @@
+/*
+ * Copyright (C) 2011 The GNOME Foundation
+ *
+ * AUTHORS:
+ * Vivien Malerba <malerba gnome-db org>
+ *
+ * This Program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <glib/gi18n-lib.h>
+#include <string.h>
+#include "ldap-classes-page.h"
+#include "classes-view.h"
+#include "class-properties.h"
+#include "../dnd.h"
+#include "../support.h"
+#include "../cc-gray-bar.h"
+#include "../browser-page.h"
+#include "../browser-stock-icons.h"
+#include "../browser-window.h"
+#include "../browser-connection.h"
+#include <virtual/gda-ldap-connection.h>
+#include "mgr-ldap-classes.h"
+#include <libgda-ui/gdaui-tree-store.h>
+
+typedef struct {
+ gchar *classname;
+ GtkTreeRowReference *reference;
+} HistoryItem;
+
+static void
+history_item_free (HistoryItem *item)
+{
+ g_free (item->classname);
+ gtk_tree_row_reference_free (item->reference);
+ g_free (item);
+}
+
+struct _LdapClassesPagePrivate {
+ BrowserConnection *bcnc;
+
+ GtkWidget *classes_view;
+ GtkWidget *entry_props;
+
+ GtkActionGroup *agroup;
+ GArray *history_items; /* array of @HistoryItem */
+ guint history_max_len; /* max allowed length of @history_items */
+ gint current_hitem; /* index in @history_items, or -1 */
+ gboolean add_hist_item;
+};
+
+static void ldap_classes_page_class_init (LdapClassesPageClass *klass);
+static void ldap_classes_page_init (LdapClassesPage *ebrowser, LdapClassesPageClass *klass);
+static void ldap_classes_page_dispose (GObject *object);
+static void ldap_classes_page_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void ldap_classes_page_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec);
+/* BrowserPage interface */
+static void ldap_classes_page_page_init (BrowserPageIface *iface);
+static GtkActionGroup *ldap_classes_page_page_get_actions_group (BrowserPage *page);
+static const gchar *ldap_classes_page_page_get_actions_ui (BrowserPage *page);
+static GtkWidget *ldap_classes_page_page_get_tab_label (BrowserPage *page, GtkWidget **out_close_button);
+
+
+/* properties */
+enum {
+ PROP_0,
+};
+
+static GObjectClass *parent_class = NULL;
+
+
+/*
+ * LdapClassesPage class implementation
+ */
+
+static void
+ldap_classes_page_class_init (LdapClassesPageClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ /* Properties */
+ object_class->set_property = ldap_classes_page_set_property;
+ object_class->get_property = ldap_classes_page_get_property;
+
+ object_class->dispose = ldap_classes_page_dispose;
+}
+
+static void
+ldap_classes_page_page_init (BrowserPageIface *iface)
+{
+ iface->i_get_actions_group = ldap_classes_page_page_get_actions_group;
+ iface->i_get_actions_ui = ldap_classes_page_page_get_actions_ui;
+ iface->i_get_tab_label = ldap_classes_page_page_get_tab_label;
+}
+
+static void
+ldap_classes_page_init (LdapClassesPage *ebrowser, G_GNUC_UNUSED LdapClassesPageClass *klass)
+{
+ ebrowser->priv = g_new0 (LdapClassesPagePrivate, 1);
+ ebrowser->priv->history_items = g_array_new (FALSE, FALSE, sizeof (HistoryItem*));
+ ebrowser->priv->history_max_len = 20;
+ ebrowser->priv->add_hist_item = TRUE;
+}
+
+static void
+ldap_classes_page_dispose (GObject *object)
+{
+ LdapClassesPage *ebrowser = (LdapClassesPage *) object;
+
+ /* free memory */
+ if (ebrowser->priv) {
+ if (ebrowser->priv->bcnc)
+ g_object_unref (ebrowser->priv->bcnc);
+ if (ebrowser->priv->agroup)
+ g_object_unref (ebrowser->priv->agroup);
+ if (ebrowser->priv->history_items) {
+ guint i;
+ for (i = 0; i < ebrowser->priv->history_items->len; i++) {
+ HistoryItem *hitem = g_array_index (ebrowser->priv->history_items,
+ HistoryItem*, i);
+ history_item_free (hitem);
+ }
+ g_array_free (ebrowser->priv->history_items, TRUE);
+ }
+ g_free (ebrowser->priv);
+ ebrowser->priv = NULL;
+ }
+
+ parent_class->dispose (object);
+}
+
+GType
+ldap_classes_page_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0)) {
+ static const GTypeInfo info = {
+ sizeof (LdapClassesPageClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) ldap_classes_page_class_init,
+ NULL,
+ NULL,
+ sizeof (LdapClassesPage),
+ 0,
+ (GInstanceInitFunc) ldap_classes_page_init,
+ 0
+ };
+
+ static GInterfaceInfo page_info = {
+ (GInterfaceInitFunc) ldap_classes_page_page_init,
+ NULL,
+ NULL
+ };
+
+ type = g_type_register_static (GTK_TYPE_VBOX, "LdapClassesPage", &info, 0);
+ g_type_add_interface_static (type, BROWSER_PAGE_TYPE, &page_info);
+ }
+ return type;
+}
+
+static void
+ldap_classes_page_set_property (GObject *object,
+ guint param_id,
+ G_GNUC_UNUSED const GValue *value,
+ GParamSpec *pspec)
+{
+ LdapClassesPage *ebrowser;
+ ebrowser = LDAP_CLASSES_PAGE (object);
+ switch (param_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+static void
+ldap_classes_page_get_property (GObject *object,
+ guint param_id,
+ G_GNUC_UNUSED GValue *value,
+ GParamSpec *pspec)
+{
+ LdapClassesPage *ebrowser;
+ ebrowser = LDAP_CLASSES_PAGE (object);
+ switch (param_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+static gchar *
+ldap_classes_page_to_selection (G_GNUC_UNUSED LdapClassesPage *ebrowser)
+{
+ const gchar *current_classname;
+ current_classname = classes_view_get_current_class (CLASSES_VIEW (ebrowser->priv->classes_view));
+ if (current_classname)
+ return g_strdup (current_classname);
+ else
+ return NULL;
+}
+
+static void
+source_drag_data_get_cb (G_GNUC_UNUSED GtkWidget *widget, G_GNUC_UNUSED GdkDragContext *context,
+ GtkSelectionData *selection_data,
+ guint browser, G_GNUC_UNUSED guint time, LdapClassesPage *ebrowser)
+{
+ switch (browser) {
+ case TARGET_KEY_VALUE: {
+ gchar *str;
+ str = ldap_classes_page_to_selection (ebrowser);
+ gtk_selection_data_set (selection_data,
+ gtk_selection_data_get_target (selection_data), 8, (guchar*) str,
+ strlen (str));
+ g_free (str);
+ break;
+ }
+ default:
+ case TARGET_PLAIN: {
+ gtk_selection_data_set_text (selection_data, ldap_classes_page_get_current_class (ebrowser), -1);
+ break;
+ }
+ case TARGET_ROOTWIN:
+ TO_IMPLEMENT; /* dropping on the Root Window => create a file */
+ break;
+ }
+}
+
+static void
+update_history_actions (LdapClassesPage *ebrowser)
+{
+ if (!ebrowser->priv->agroup)
+ return;
+
+ gboolean is_first = TRUE;
+ gboolean is_last = TRUE;
+ const gchar *current_classname;
+ ebrowser->priv->current_hitem = -1;
+ current_classname = ldap_classes_page_get_current_class (ebrowser);
+ if (current_classname) {
+ guint i;
+ for (i = 0; i < ebrowser->priv->history_items->len; i++) {
+ HistoryItem *hitem;
+ hitem = g_array_index (ebrowser->priv->history_items, HistoryItem*, i);
+ if (!strcmp (hitem->classname, current_classname)) {
+ if (i != 0)
+ is_first = FALSE;
+ if (i+1 < ebrowser->priv->history_items->len)
+ is_last = FALSE;
+ ebrowser->priv->current_hitem = (gint) i;
+ break;
+ }
+ }
+ }
+
+ GtkAction *action;
+ action = gtk_action_group_get_action (ebrowser->priv->agroup, "DnBack");
+ gtk_action_set_sensitive (action, !is_first);
+ action = gtk_action_group_get_action (ebrowser->priv->agroup, "DnForward");
+ gtk_action_set_sensitive (action, !is_last);
+}
+
+static void
+selection_changed_cb (GtkTreeSelection *sel, LdapClassesPage *ebrowser)
+{
+ const gchar *current_classname;
+ current_classname = classes_view_get_current_class (CLASSES_VIEW (ebrowser->priv->classes_view));
+ class_properties_set_class (CLASS_PROPERTIES (ebrowser->priv->entry_props), current_classname);
+
+ if (!current_classname)
+ return;
+
+ if (ebrowser->priv->agroup) {
+ GtkAction *action;
+ action = gtk_action_group_get_action (ebrowser->priv->agroup, "AddToFav");
+ current_classname = ldap_classes_page_get_current_class (ebrowser);
+ gtk_action_set_sensitive (action, (current_classname && *current_classname) ? TRUE : FALSE);
+ }
+
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ if (ebrowser->priv->add_hist_item && gtk_tree_selection_get_selected (sel, &model, &iter)) {
+ GtkTreePath *path;
+ HistoryItem *hitem;
+ hitem = g_new (HistoryItem, 1);
+ hitem->classname = g_strdup (current_classname);
+
+ path = gtk_tree_model_get_path (model, &iter);
+ if (path) {
+ hitem->reference = gtk_tree_row_reference_new (model, path);
+ gtk_tree_path_free (path);
+ }
+ else
+ hitem->reference = NULL;
+
+ if (ebrowser->priv->current_hitem >= 0) {
+ guint i;
+ for (i = (guint) ebrowser->priv->current_hitem + 1;
+ i < ebrowser->priv->history_items->len; ) {
+ HistoryItem *tmphitem;
+ tmphitem = g_array_index (ebrowser->priv->history_items, HistoryItem*, i);
+ history_item_free (tmphitem);
+ g_array_remove_index (ebrowser->priv->history_items, i);
+ }
+ }
+ g_array_append_val (ebrowser->priv->history_items, hitem);
+ if (ebrowser->priv->history_items->len > ebrowser->priv->history_max_len) {
+ hitem = g_array_index (ebrowser->priv->history_items, HistoryItem*, 0);
+ history_item_free (hitem);
+ g_array_remove_index (ebrowser->priv->history_items, 0);
+ }
+ ebrowser->priv->current_hitem = ebrowser->priv->history_items->len -1;
+ }
+ update_history_actions (ebrowser);
+}
+
+static void
+open_classname_requested_cb (ClassProperties *eprop, const gchar *classname, LdapClassesPage *ebrowser)
+{
+ ldap_classes_page_set_current_class (ebrowser, classname);
+}
+
+/**
+ * ldap_classes_page_new:
+ *
+ * Returns: a new #GtkWidget
+ */
+GtkWidget *
+ldap_classes_page_new (BrowserConnection *bcnc, const gchar *classname)
+{
+ LdapClassesPage *ebrowser;
+
+ g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), NULL);
+
+ ebrowser = LDAP_CLASSES_PAGE (g_object_new (LDAP_CLASSES_PAGE_TYPE, NULL));
+ ebrowser->priv->bcnc = g_object_ref ((GObject*) bcnc);
+
+ /* header */
+ GtkWidget *label;
+ gchar *str;
+
+ str = g_strdup_printf ("<b>%s</b>", _("LDAP classes browser"));
+ label = cc_gray_bar_new (str);
+ g_free (str);
+ gtk_box_pack_start (GTK_BOX (ebrowser), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+ g_signal_connect (label, "drag-data-get",
+ G_CALLBACK (source_drag_data_get_cb), ebrowser);
+
+ /* VPaned widget */
+ GtkWidget *hp;
+ hp = gtk_hpaned_new ();
+ gtk_box_pack_start (GTK_BOX (ebrowser), hp, TRUE, TRUE, 0);
+
+ /* tree */
+ GtkWidget *vbox, *hview, *sw;
+ gfloat yalign;
+
+ vbox = gtk_vbox_new (FALSE, FALSE);
+ gtk_paned_add1 (GTK_PANED (hp), vbox);
+
+ str = g_strdup_printf ("<b>%s:</b>", _("LDAP classes"));
+ label = gtk_label_new ("");
+ gtk_label_set_markup (GTK_LABEL (label), str);
+ g_free (str);
+ gtk_misc_get_alignment (GTK_MISC (label), NULL, &yalign);
+ gtk_misc_set_alignment (GTK_MISC (label), 0., yalign);
+ gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+
+ hview = classes_view_new (bcnc, NULL);
+ ebrowser->priv->classes_view = hview;
+ sw = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+ gtk_container_add (GTK_CONTAINER (sw), hview);
+ gtk_box_pack_start (GTK_BOX (vbox), sw, TRUE, TRUE, 0);
+
+ /* tree selection */
+ GtkTreeSelection *sel;
+ sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (ebrowser->priv->classes_view));
+ gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
+ g_signal_connect (sel, "changed",
+ G_CALLBACK (selection_changed_cb), ebrowser);
+
+ /* details */
+ vbox = gtk_vbox_new (FALSE, FALSE);
+ gtk_paned_add2 (GTK_PANED (hp), vbox);
+
+ str = g_strdup_printf ("<b>%s:</b>", _("LDAP class's properties"));
+ label = gtk_label_new ("");
+ gtk_label_set_markup (GTK_LABEL (label), str);
+ g_free (str);
+ gtk_misc_get_alignment (GTK_MISC (label), NULL, &yalign);
+ gtk_misc_set_alignment (GTK_MISC (label), 0., yalign);
+ gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+
+ GtkWidget *props;
+ props = class_properties_new (bcnc);
+ gtk_box_pack_start (GTK_BOX (vbox), props, TRUE, TRUE, 0);
+ ebrowser->priv->entry_props = props;
+ g_signal_connect (props, "open-class",
+ G_CALLBACK (open_classname_requested_cb), ebrowser);
+
+ gtk_paned_set_position (GTK_PANED (hp), 250);
+
+ gtk_widget_show_all (hp);
+
+ if (classname)
+ classes_view_set_current_class (CLASSES_VIEW (ebrowser->priv->classes_view), classname);
+
+ return (GtkWidget*) ebrowser;
+}
+
+/**
+ * ldap_classes_page_get_current_class:
+ */
+const gchar *
+ldap_classes_page_get_current_class (LdapClassesPage *ldap_classes_page)
+{
+ g_return_val_if_fail (IS_LDAP_CLASSES_PAGE (ldap_classes_page), NULL);
+ return classes_view_get_current_class (CLASSES_VIEW (ldap_classes_page->priv->classes_view));
+}
+
+/**
+ * ldap_classes_page_set_current_class:
+ */
+void
+ldap_classes_page_set_current_class (LdapClassesPage *ldap_classes_page, const gchar *classname)
+{
+ g_return_if_fail (IS_LDAP_CLASSES_PAGE (ldap_classes_page));
+ classes_view_set_current_class (CLASSES_VIEW (ldap_classes_page->priv->classes_view), classname);
+}
+
+
+/*
+ * UI actions
+ */
+static void
+action_add_to_fav_cb (G_GNUC_UNUSED GtkAction *action, LdapClassesPage *ebrowser)
+{
+ BrowserFavorites *bfav;
+ BrowserFavoritesAttributes fav;
+ GError *error = NULL;
+ const gchar *tmp;
+
+ tmp = classes_view_get_current_class (CLASSES_VIEW (ebrowser->priv->classes_view));
+ memset (&fav, 0, sizeof (BrowserFavoritesAttributes));
+ fav.id = -1;
+ fav.type = BROWSER_FAVORITES_LDAP_CLASS;
+ fav.name = ldap_classes_page_to_selection (ebrowser);
+ fav.descr = NULL;
+ fav.contents = ldap_classes_page_to_selection (ebrowser);
+
+ bfav = browser_connection_get_favorites (ebrowser->priv->bcnc);
+ if (! browser_favorites_add (bfav, 0, &fav, ORDER_KEY_LDAP, G_MAXINT, &error)) {
+ browser_show_error ((GtkWindow*) gtk_widget_get_toplevel ((GtkWidget*) ebrowser),
+ _("Could not add favorite: %s"),
+ error && error->message ? error->message : _("No detail"));
+ if (error)
+ g_error_free (error);
+ }
+ g_free (fav.contents);
+}
+
+static void
+action_class_back_cb (G_GNUC_UNUSED GtkAction *action, LdapClassesPage *ebrowser)
+{
+ ebrowser->priv->add_hist_item = FALSE;
+ if (ebrowser->priv->current_hitem > 0) {
+ HistoryItem *hitem = NULL;
+ gboolean use_dn = TRUE;
+ hitem = g_array_index (ebrowser->priv->history_items, HistoryItem*,
+ ebrowser->priv->current_hitem - 1);
+ if (hitem->reference) {
+ if (gtk_tree_row_reference_valid (hitem->reference)) {
+ GtkTreeSelection *sel;
+ GtkTreePath *path;
+ path = gtk_tree_row_reference_get_path (hitem->reference);
+ sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (ebrowser->priv->classes_view));
+ gtk_tree_selection_select_path (sel, path);
+ gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (ebrowser->priv->classes_view),
+ path, NULL, TRUE, 0.5, 0.5);
+ gtk_tree_path_free (path);
+ use_dn = FALSE;
+ }
+ else {
+ gtk_tree_row_reference_free (hitem->reference);
+ hitem->reference = NULL;
+ }
+ }
+ if (use_dn)
+ classes_view_set_current_class (CLASSES_VIEW (ebrowser->priv->classes_view),
+ hitem->classname);
+ }
+ ebrowser->priv->add_hist_item = TRUE;
+}
+
+static void
+action_class_forward_cb (G_GNUC_UNUSED GtkAction *action, LdapClassesPage *ebrowser)
+{
+ ebrowser->priv->add_hist_item = FALSE;
+ if ((ebrowser->priv->current_hitem >=0) &&
+ ((guint) ebrowser->priv->current_hitem < ebrowser->priv->history_items->len)) {
+ HistoryItem *hitem = NULL;
+ gboolean use_dn = TRUE;
+ hitem = g_array_index (ebrowser->priv->history_items, HistoryItem*,
+ ebrowser->priv->current_hitem + 1);
+ if (hitem->reference) {
+ if (gtk_tree_row_reference_valid (hitem->reference)) {
+ GtkTreeSelection *sel;
+ GtkTreePath *path;
+ path = gtk_tree_row_reference_get_path (hitem->reference);
+ sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (ebrowser->priv->classes_view));
+ gtk_tree_selection_select_path (sel, path);
+ gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (ebrowser->priv->classes_view),
+ path, NULL, TRUE, 0.5, 0.5);
+ gtk_tree_path_free (path);
+ use_dn = FALSE;
+ }
+ else {
+ gtk_tree_row_reference_free (hitem->reference);
+ hitem->reference = NULL;
+ }
+ }
+ if (use_dn)
+ classes_view_set_current_class (CLASSES_VIEW (ebrowser->priv->classes_view),
+ hitem->classname);
+ }
+ ebrowser->priv->add_hist_item = TRUE;
+}
+
+static GtkActionEntry ui_actions[] = {
+ { "LDAP", NULL, N_("_LDAP"), NULL, N_("LDAP"), NULL },
+ { "AddToFav", STOCK_ADD_BOOKMARK, N_("Add to _Favorites"), NULL, N_("Add class to favorites"),
+ G_CALLBACK (action_add_to_fav_cb)},
+ { "DnBack", GTK_STOCK_GO_BACK, N_("Previous class"), NULL, N_("Move back to previous LDAP class"),
+ G_CALLBACK (action_class_back_cb)},
+ { "DnForward", GTK_STOCK_GO_FORWARD, N_("Next class"), NULL, N_("Move to next LDAP class"),
+ G_CALLBACK (action_class_forward_cb)},
+};
+static const gchar *ui_actions_browser =
+ "<ui>"
+ " <menubar name='MenuBar'>"
+ " <placeholder name='MenuExtension'>"
+ " <menu name='LDAP' action='LDAP'>"
+ " <menuitem name='AddToFav' action= 'AddToFav'/>"
+ " <separator/>"
+ " <menuitem name='DnBack' action= 'DnBack'/>"
+ " <menuitem name='DnForward' action= 'DnForward'/>"
+ " </menu>"
+ " </placeholder>"
+ " </menubar>"
+ " <toolbar name='ToolBar'>"
+ " <separator/>"
+ " <toolitem action='AddToFav'/>"
+ " <toolitem action='DnBack'/>"
+ " <toolitem action='DnForward'/>"
+ " </toolbar>"
+ "</ui>";
+
+static GtkActionGroup *
+ldap_classes_page_page_get_actions_group (BrowserPage *page)
+{
+ LdapClassesPage *ebrowser;
+
+ ebrowser = LDAP_CLASSES_PAGE (page);
+ if (! ebrowser->priv->agroup) {
+ GtkActionGroup *agroup;
+ agroup = gtk_action_group_new ("LdapLdapClassesPageActions");
+ gtk_action_group_set_translation_domain (agroup, GETTEXT_PACKAGE);
+ gtk_action_group_add_actions (agroup, ui_actions, G_N_ELEMENTS (ui_actions), page);
+ ebrowser->priv->agroup = agroup;
+
+ GtkAction *action;
+ const gchar *current_classname;
+ action = gtk_action_group_get_action (agroup, "AddToFav");
+ current_classname = ldap_classes_page_get_current_class (ebrowser);
+ gtk_action_set_sensitive (action, (current_classname && *current_classname) ? TRUE : FALSE);
+
+ update_history_actions (ebrowser);
+ }
+
+ return g_object_ref (ebrowser->priv->agroup);
+}
+
+static const gchar *
+ldap_classes_page_page_get_actions_ui (G_GNUC_UNUSED BrowserPage *page)
+{
+ return ui_actions_browser;
+}
+
+static GtkWidget *
+ldap_classes_page_page_get_tab_label (BrowserPage *page, GtkWidget **out_close_button)
+{
+ LdapClassesPage *ebrowser;
+ const gchar *tab_name;
+ GdkPixbuf *classes_pixbuf;
+
+ ebrowser = LDAP_CLASSES_PAGE (page);
+ classes_pixbuf = browser_get_pixbuf_icon (BROWSER_ICON_LDAP_CLASS_STRUCTURAL);
+ tab_name = _("LDAP classes");
+ return browser_make_tab_label_with_pixbuf (tab_name,
+ classes_pixbuf,
+ out_close_button ? TRUE : FALSE, out_close_button);
+}
diff --git a/tools/browser/ldap-browser/ldap-classes-page.h b/tools/browser/ldap-browser/ldap-classes-page.h
new file mode 100644
index 0000000..59c2bd9
--- /dev/null
+++ b/tools/browser/ldap-browser/ldap-classes-page.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2011 The GNOME Foundation
+ *
+ * AUTHORS:
+ * Vivien Malerba <malerba gnome-db org>
+ *
+ * This Program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __LDAP_CLASSES_PAGE_H__
+#define __LDAP_CLASSES_PAGE_H__
+
+#include <gtk/gtk.h>
+#include "../browser-connection.h"
+
+G_BEGIN_DECLS
+
+#define LDAP_CLASSES_PAGE_TYPE (ldap_classes_page_get_type())
+#define LDAP_CLASSES_PAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, LDAP_CLASSES_PAGE_TYPE, LdapClassesPage))
+#define LDAP_CLASSES_PAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, LDAP_CLASSES_PAGE_TYPE, LdapClassesPageClass))
+#define IS_LDAP_CLASSES_PAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, LDAP_CLASSES_PAGE_TYPE))
+#define IS_LDAP_CLASSES_PAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), LDAP_CLASSES_PAGE_TYPE))
+
+typedef struct _LdapClassesPage LdapClassesPage;
+typedef struct _LdapClassesPageClass LdapClassesPageClass;
+typedef struct _LdapClassesPagePrivate LdapClassesPagePrivate;
+
+struct _LdapClassesPage {
+ GtkVBox parent;
+ LdapClassesPagePrivate *priv;
+};
+
+struct _LdapClassesPageClass {
+ GtkVBoxClass parent_class;
+};
+
+GType ldap_classes_page_get_type (void) G_GNUC_CONST;
+
+GtkWidget *ldap_classes_page_new (BrowserConnection *bcnc, const gchar *dn);
+const gchar *ldap_classes_page_get_current_class (LdapClassesPage *ldap_classes_page);
+void ldap_classes_page_set_current_class (LdapClassesPage *ldap_classes_page, const gchar *classname);
+
+G_END_DECLS
+
+#endif
diff --git a/tools/browser/ldap-browser/ldap-entries-page.c b/tools/browser/ldap-browser/ldap-entries-page.c
new file mode 100644
index 0000000..42cc7c8
--- /dev/null
+++ b/tools/browser/ldap-browser/ldap-entries-page.c
@@ -0,0 +1,632 @@
+/*
+ * Copyright (C) 2011 The GNOME Foundation
+ *
+ * AUTHORS:
+ * Vivien Malerba <malerba gnome-db org>
+ *
+ * This Program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <glib/gi18n-lib.h>
+#include <string.h>
+#include "ldap-entries-page.h"
+#include "hierarchy-view.h"
+#include "entry-properties.h"
+#include "../dnd.h"
+#include "../support.h"
+#include "../cc-gray-bar.h"
+#include "../browser-page.h"
+#include "../browser-stock-icons.h"
+#include "../browser-window.h"
+#include "../browser-connection.h"
+#include <virtual/gda-ldap-connection.h>
+#include "mgr-ldap-entries.h"
+#include <libgda-ui/gdaui-tree-store.h>
+#include "ldap-browser-perspective.h"
+
+typedef struct {
+ gchar *dn;
+ GtkTreeRowReference *reference;
+} HistoryItem;
+
+static void
+history_item_free (HistoryItem *item)
+{
+ g_free (item->dn);
+ gtk_tree_row_reference_free (item->reference);
+ g_free (item);
+}
+
+struct _LdapEntriesPagePrivate {
+ BrowserConnection *bcnc;
+
+ GtkWidget *entries_view;
+ GtkWidget *entry_props;
+
+ GtkActionGroup *agroup;
+ GArray *history_items; /* array of @HistoryItem */
+ guint history_max_len; /* max allowed length of @history_items */
+ gint current_hitem; /* index in @history_items, or -1 */
+ gboolean add_hist_item;
+};
+
+static void ldap_entries_page_class_init (LdapEntriesPageClass *klass);
+static void ldap_entries_page_init (LdapEntriesPage *ebrowser, LdapEntriesPageClass *klass);
+static void ldap_entries_page_dispose (GObject *object);
+static void ldap_entries_page_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void ldap_entries_page_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec);
+/* BrowserPage interface */
+static void ldap_entries_page_page_init (BrowserPageIface *iface);
+static GtkActionGroup *ldap_entries_page_page_get_actions_group (BrowserPage *page);
+static const gchar *ldap_entries_page_page_get_actions_ui (BrowserPage *page);
+static GtkWidget *ldap_entries_page_page_get_tab_label (BrowserPage *page, GtkWidget **out_close_button);
+
+
+/* properties */
+enum {
+ PROP_0,
+};
+
+static GObjectClass *parent_class = NULL;
+
+
+/*
+ * LdapEntriesPage class implementation
+ */
+
+static void
+ldap_entries_page_class_init (LdapEntriesPageClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ /* Properties */
+ object_class->set_property = ldap_entries_page_set_property;
+ object_class->get_property = ldap_entries_page_get_property;
+
+ object_class->dispose = ldap_entries_page_dispose;
+}
+
+static void
+ldap_entries_page_page_init (BrowserPageIface *iface)
+{
+ iface->i_get_actions_group = ldap_entries_page_page_get_actions_group;
+ iface->i_get_actions_ui = ldap_entries_page_page_get_actions_ui;
+ iface->i_get_tab_label = ldap_entries_page_page_get_tab_label;
+}
+
+static void
+ldap_entries_page_init (LdapEntriesPage *ebrowser, G_GNUC_UNUSED LdapEntriesPageClass *klass)
+{
+ ebrowser->priv = g_new0 (LdapEntriesPagePrivate, 1);
+ ebrowser->priv->history_items = g_array_new (FALSE, FALSE, sizeof (HistoryItem*));
+ ebrowser->priv->history_max_len = 20;
+ ebrowser->priv->add_hist_item = TRUE;
+}
+
+static void
+ldap_entries_page_dispose (GObject *object)
+{
+ LdapEntriesPage *ebrowser = (LdapEntriesPage *) object;
+
+ /* free memory */
+ if (ebrowser->priv) {
+ if (ebrowser->priv->bcnc)
+ g_object_unref (ebrowser->priv->bcnc);
+ if (ebrowser->priv->agroup)
+ g_object_unref (ebrowser->priv->agroup);
+ if (ebrowser->priv->history_items) {
+ guint i;
+ for (i = 0; i < ebrowser->priv->history_items->len; i++) {
+ HistoryItem *hitem = g_array_index (ebrowser->priv->history_items,
+ HistoryItem*, i);
+ history_item_free (hitem);
+ }
+ g_array_free (ebrowser->priv->history_items, TRUE);
+ }
+ g_free (ebrowser->priv);
+ ebrowser->priv = NULL;
+ }
+
+ parent_class->dispose (object);
+}
+
+GType
+ldap_entries_page_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0)) {
+ static const GTypeInfo info = {
+ sizeof (LdapEntriesPageClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) ldap_entries_page_class_init,
+ NULL,
+ NULL,
+ sizeof (LdapEntriesPage),
+ 0,
+ (GInstanceInitFunc) ldap_entries_page_init,
+ 0
+ };
+
+ static GInterfaceInfo page_info = {
+ (GInterfaceInitFunc) ldap_entries_page_page_init,
+ NULL,
+ NULL
+ };
+
+ type = g_type_register_static (GTK_TYPE_VBOX, "LdapEntriesPage", &info, 0);
+ g_type_add_interface_static (type, BROWSER_PAGE_TYPE, &page_info);
+ }
+ return type;
+}
+
+static void
+ldap_entries_page_set_property (GObject *object,
+ guint param_id,
+ G_GNUC_UNUSED const GValue *value,
+ GParamSpec *pspec)
+{
+ LdapEntriesPage *ebrowser;
+ ebrowser = LDAP_ENTRIES_PAGE (object);
+ switch (param_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+static void
+ldap_entries_page_get_property (GObject *object,
+ guint param_id,
+ G_GNUC_UNUSED GValue *value,
+ GParamSpec *pspec)
+{
+ LdapEntriesPage *ebrowser;
+ ebrowser = LDAP_ENTRIES_PAGE (object);
+ switch (param_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+static gchar *
+ldap_entries_page_to_selection (G_GNUC_UNUSED LdapEntriesPage *ebrowser)
+{
+ const gchar *current_dn;
+ current_dn = hierarchy_view_get_current_dn (HIERARCHY_VIEW (ebrowser->priv->entries_view), NULL);
+ if (current_dn)
+ return g_strdup (current_dn);
+ else
+ return NULL;
+}
+
+static void
+source_drag_data_get_cb (G_GNUC_UNUSED GtkWidget *widget, G_GNUC_UNUSED GdkDragContext *context,
+ GtkSelectionData *selection_data,
+ guint browser, G_GNUC_UNUSED guint time, LdapEntriesPage *ebrowser)
+{
+ switch (browser) {
+ case TARGET_KEY_VALUE: {
+ gchar *str;
+ str = ldap_entries_page_to_selection (ebrowser);
+ gtk_selection_data_set (selection_data,
+ gtk_selection_data_get_target (selection_data), 8, (guchar*) str,
+ strlen (str));
+ g_free (str);
+ break;
+ }
+ default:
+ case TARGET_PLAIN: {
+ gtk_selection_data_set_text (selection_data, ldap_entries_page_get_current_dn (ebrowser), -1);
+ break;
+ }
+ case TARGET_ROOTWIN:
+ TO_IMPLEMENT; /* dropping on the Root Window => create a file */
+ break;
+ }
+}
+
+static void
+update_history_actions (LdapEntriesPage *ebrowser)
+{
+ if (!ebrowser->priv->agroup)
+ return;
+
+ gboolean is_first = TRUE;
+ gboolean is_last = TRUE;
+ const gchar *current_dn;
+ ebrowser->priv->current_hitem = -1;
+ current_dn = ldap_entries_page_get_current_dn (ebrowser);
+ if (current_dn) {
+ guint i;
+ for (i = 0; i < ebrowser->priv->history_items->len; i++) {
+ HistoryItem *hitem;
+ hitem = g_array_index (ebrowser->priv->history_items, HistoryItem*, i);
+ if (!strcmp (hitem->dn, current_dn)) {
+ if (i != 0)
+ is_first = FALSE;
+ if (i+1 < ebrowser->priv->history_items->len)
+ is_last = FALSE;
+ ebrowser->priv->current_hitem = (gint) i;
+ break;
+ }
+ }
+ }
+
+ GtkAction *action;
+ action = gtk_action_group_get_action (ebrowser->priv->agroup, "DnBack");
+ gtk_action_set_sensitive (action, !is_first);
+ action = gtk_action_group_get_action (ebrowser->priv->agroup, "DnForward");
+ gtk_action_set_sensitive (action, !is_last);
+}
+
+static void
+selection_changed_cb (GtkTreeSelection *sel, LdapEntriesPage *ebrowser)
+{
+ const gchar *current_dn;
+ current_dn = hierarchy_view_get_current_dn (HIERARCHY_VIEW (ebrowser->priv->entries_view), NULL);
+ entry_properties_set_dn (ENTRY_PROPERTIES (ebrowser->priv->entry_props), current_dn);
+
+ if (ebrowser->priv->agroup) {
+ GtkAction *action;
+ action = gtk_action_group_get_action (ebrowser->priv->agroup, "AddToFav");
+ current_dn = ldap_entries_page_get_current_dn (ebrowser);
+ gtk_action_set_sensitive (action, (current_dn && *current_dn) ? TRUE : FALSE);
+ }
+
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ if (ebrowser->priv->add_hist_item && gtk_tree_selection_get_selected (sel, &model, &iter)) {
+ GtkTreePath *path;
+ HistoryItem *hitem;
+ hitem = g_new (HistoryItem, 1);
+ hitem->dn = g_strdup (current_dn);
+
+ path = gtk_tree_model_get_path (model, &iter);
+ if (path) {
+ hitem->reference = gtk_tree_row_reference_new (model, path);
+ gtk_tree_path_free (path);
+ }
+ else
+ hitem->reference = NULL;
+
+ if (ebrowser->priv->current_hitem >= 0) {
+ guint i;
+ for (i = (guint) ebrowser->priv->current_hitem + 1;
+ i < ebrowser->priv->history_items->len; ) {
+ HistoryItem *tmphitem;
+ tmphitem = g_array_index (ebrowser->priv->history_items, HistoryItem*, i);
+ history_item_free (tmphitem);
+ g_array_remove_index (ebrowser->priv->history_items, i);
+ }
+ }
+ g_array_append_val (ebrowser->priv->history_items, hitem);
+ if (ebrowser->priv->history_items->len > ebrowser->priv->history_max_len) {
+ hitem = g_array_index (ebrowser->priv->history_items, HistoryItem*, 0);
+ history_item_free (hitem);
+ g_array_remove_index (ebrowser->priv->history_items, 0);
+ }
+ ebrowser->priv->current_hitem = ebrowser->priv->history_items->len -1;
+ }
+ update_history_actions (ebrowser);
+}
+
+static void
+open_dn_requested_cb (EntryProperties *eprop, const gchar *dn, LdapEntriesPage *ebrowser)
+{
+ ldap_entries_page_set_current_dn (ebrowser, dn);
+}
+
+static void
+open_class_requested_cb (EntryProperties *eprop, const gchar *classname, LdapEntriesPage *ebrowser)
+{
+ BrowserPerspective *bpers;
+ bpers = browser_page_get_perspective (BROWSER_PAGE (ebrowser));
+ ldap_browser_perspective_display_ldap_class (LDAP_BROWSER_PERSPECTIVE (bpers), classname);
+}
+
+/**
+ * ldap_entries_page_new:
+ *
+ * Returns: a new #GtkWidget
+ */
+GtkWidget *
+ldap_entries_page_new (BrowserConnection *bcnc, const gchar *dn)
+{
+ LdapEntriesPage *ebrowser;
+
+ g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), NULL);
+
+ ebrowser = LDAP_ENTRIES_PAGE (g_object_new (LDAP_ENTRIES_PAGE_TYPE, NULL));
+ ebrowser->priv->bcnc = g_object_ref ((GObject*) bcnc);
+
+ /* header */
+ GtkWidget *label;
+ gchar *str;
+
+ str = g_strdup_printf ("<b>%s</b>", _("LDAP entries browser"));
+ label = cc_gray_bar_new (str);
+ g_free (str);
+ gtk_box_pack_start (GTK_BOX (ebrowser), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+ g_signal_connect (label, "drag-data-get",
+ G_CALLBACK (source_drag_data_get_cb), ebrowser);
+
+ /* VPaned widget */
+ GtkWidget *hp;
+ hp = gtk_hpaned_new ();
+ gtk_box_pack_start (GTK_BOX (ebrowser), hp, TRUE, TRUE, 0);
+
+ /* tree */
+ GtkWidget *vbox, *hview, *sw;
+ gfloat yalign;
+
+ vbox = gtk_vbox_new (FALSE, FALSE);
+ gtk_paned_add1 (GTK_PANED (hp), vbox);
+
+ str = g_strdup_printf ("<b>%s:</b>", _("LDAP hierarchy"));
+ label = gtk_label_new ("");
+ gtk_label_set_markup (GTK_LABEL (label), str);
+ g_free (str);
+ gtk_misc_get_alignment (GTK_MISC (label), NULL, &yalign);
+ gtk_misc_set_alignment (GTK_MISC (label), 0., yalign);
+ gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+
+ hview = hierarchy_view_new (bcnc, dn);
+ ebrowser->priv->entries_view = hview;
+ sw = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+ gtk_container_add (GTK_CONTAINER (sw), hview);
+ gtk_box_pack_start (GTK_BOX (vbox), sw, TRUE, TRUE, 0);
+
+ /* tree selection */
+ GtkTreeSelection *sel;
+ sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (ebrowser->priv->entries_view));
+ gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
+ g_signal_connect (sel, "changed",
+ G_CALLBACK (selection_changed_cb), ebrowser);
+
+ /* details */
+ vbox = gtk_vbox_new (FALSE, FALSE);
+ gtk_paned_add2 (GTK_PANED (hp), vbox);
+
+ str = g_strdup_printf ("<b>%s:</b>", _("LDAP entry's details"));
+ label = gtk_label_new ("");
+ gtk_label_set_markup (GTK_LABEL (label), str);
+ g_free (str);
+ gtk_misc_get_alignment (GTK_MISC (label), NULL, &yalign);
+ gtk_misc_set_alignment (GTK_MISC (label), 0., yalign);
+ gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+
+ GtkWidget *props;
+ props = entry_properties_new (bcnc);
+ gtk_box_pack_start (GTK_BOX (vbox), props, TRUE, TRUE, 0);
+ ebrowser->priv->entry_props = props;
+ g_signal_connect (props, "open-dn",
+ G_CALLBACK (open_dn_requested_cb), ebrowser);
+ g_signal_connect (props, "open-class",
+ G_CALLBACK (open_class_requested_cb), ebrowser);
+
+ gtk_paned_set_position (GTK_PANED (hp), 250);
+
+ gtk_widget_show_all (hp);
+
+ return (GtkWidget*) ebrowser;
+}
+
+/**
+ * ldap_entries_page_get_current_dn:
+ */
+const gchar *
+ldap_entries_page_get_current_dn (LdapEntriesPage *ldap_entries_page)
+{
+ g_return_val_if_fail (IS_LDAP_ENTRIES_PAGE (ldap_entries_page), NULL);
+ return hierarchy_view_get_current_dn (HIERARCHY_VIEW (ldap_entries_page->priv->entries_view), NULL);
+}
+
+/**
+ * ldap_entries_page_set_current_dn:
+ */
+void
+ldap_entries_page_set_current_dn (LdapEntriesPage *ldap_entries_page, const gchar *dn)
+{
+ g_return_if_fail (IS_LDAP_ENTRIES_PAGE (ldap_entries_page));
+ hierarchy_view_set_current_dn (HIERARCHY_VIEW (ldap_entries_page->priv->entries_view), dn);
+}
+
+
+/*
+ * UI actions
+ */
+static void
+action_add_to_fav_cb (G_GNUC_UNUSED GtkAction *action, LdapEntriesPage *ebrowser)
+{
+ BrowserFavorites *bfav;
+ BrowserFavoritesAttributes fav;
+ GError *error = NULL;
+ const gchar *tmp, *cn;
+
+ tmp = hierarchy_view_get_current_dn (HIERARCHY_VIEW (ebrowser->priv->entries_view), &cn);
+ memset (&fav, 0, sizeof (BrowserFavoritesAttributes));
+ fav.id = -1;
+ fav.type = BROWSER_FAVORITES_LDAP_DN;
+ fav.name = ldap_entries_page_to_selection (ebrowser);
+ fav.descr = (gchar*) cn;
+ fav.contents = ldap_entries_page_to_selection (ebrowser);
+
+ bfav = browser_connection_get_favorites (ebrowser->priv->bcnc);
+ if (! browser_favorites_add (bfav, 0, &fav, ORDER_KEY_LDAP, G_MAXINT, &error)) {
+ browser_show_error ((GtkWindow*) gtk_widget_get_toplevel ((GtkWidget*) ebrowser),
+ _("Could not add favorite: %s"),
+ error && error->message ? error->message : _("No detail"));
+ if (error)
+ g_error_free (error);
+ }
+ g_free (fav.contents);
+}
+
+static void
+action_dn_back_cb (G_GNUC_UNUSED GtkAction *action, LdapEntriesPage *ebrowser)
+{
+ ebrowser->priv->add_hist_item = FALSE;
+ if (ebrowser->priv->current_hitem > 0) {
+ HistoryItem *hitem = NULL;
+ gboolean use_dn = TRUE;
+ hitem = g_array_index (ebrowser->priv->history_items, HistoryItem*,
+ ebrowser->priv->current_hitem - 1);
+ if (hitem->reference) {
+ if (gtk_tree_row_reference_valid (hitem->reference)) {
+ GtkTreeSelection *sel;
+ GtkTreePath *path;
+ path = gtk_tree_row_reference_get_path (hitem->reference);
+ sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (ebrowser->priv->entries_view));
+ gtk_tree_selection_select_path (sel, path);
+ gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (ebrowser->priv->entries_view),
+ path, NULL, TRUE, 0.5, 0.5);
+ gtk_tree_path_free (path);
+ use_dn = FALSE;
+ }
+ else {
+ gtk_tree_row_reference_free (hitem->reference);
+ hitem->reference = NULL;
+ }
+ }
+ if (use_dn)
+ hierarchy_view_set_current_dn (HIERARCHY_VIEW (ebrowser->priv->entries_view),
+ hitem->dn);
+ }
+ ebrowser->priv->add_hist_item = TRUE;
+}
+
+static void
+action_dn_forward_cb (G_GNUC_UNUSED GtkAction *action, LdapEntriesPage *ebrowser)
+{
+ ebrowser->priv->add_hist_item = FALSE;
+ if ((ebrowser->priv->current_hitem >=0) &&
+ ((guint) ebrowser->priv->current_hitem < ebrowser->priv->history_items->len)) {
+ HistoryItem *hitem = NULL;
+ gboolean use_dn = TRUE;
+ hitem = g_array_index (ebrowser->priv->history_items, HistoryItem*,
+ ebrowser->priv->current_hitem + 1);
+ if (hitem->reference) {
+ if (gtk_tree_row_reference_valid (hitem->reference)) {
+ GtkTreeSelection *sel;
+ GtkTreePath *path;
+ path = gtk_tree_row_reference_get_path (hitem->reference);
+ sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (ebrowser->priv->entries_view));
+ gtk_tree_selection_select_path (sel, path);
+ gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (ebrowser->priv->entries_view),
+ path, NULL, TRUE, 0.5, 0.5);
+ gtk_tree_path_free (path);
+ use_dn = FALSE;
+ }
+ else {
+ gtk_tree_row_reference_free (hitem->reference);
+ hitem->reference = NULL;
+ }
+ }
+ if (use_dn)
+ hierarchy_view_set_current_dn (HIERARCHY_VIEW (ebrowser->priv->entries_view),
+ hitem->dn);
+ }
+ ebrowser->priv->add_hist_item = TRUE;
+}
+
+static GtkActionEntry ui_actions[] = {
+ { "LDAP", NULL, N_("_LDAP"), NULL, N_("LDAP"), NULL },
+ { "AddToFav", STOCK_ADD_BOOKMARK, N_("Add to _Favorites"), NULL, N_("Add entry to favorites"),
+ G_CALLBACK (action_add_to_fav_cb)},
+ { "DnBack", GTK_STOCK_GO_BACK, N_("Previous entry"), NULL, N_("Move back to previous LDAP entry"),
+ G_CALLBACK (action_dn_back_cb)},
+ { "DnForward", GTK_STOCK_GO_FORWARD, N_("Next entry"), NULL, N_("Move to next LDAP entry"),
+ G_CALLBACK (action_dn_forward_cb)},
+};
+static const gchar *ui_actions_browser =
+ "<ui>"
+ " <menubar name='MenuBar'>"
+ " <placeholder name='MenuExtension'>"
+ " <menu name='LDAP' action='LDAP'>"
+ " <menuitem name='AddToFav' action= 'AddToFav'/>"
+ " <separator/>"
+ " <menuitem name='DnBack' action= 'DnBack'/>"
+ " <menuitem name='DnForward' action= 'DnForward'/>"
+ " </menu>"
+ " </placeholder>"
+ " </menubar>"
+ " <toolbar name='ToolBar'>"
+ " <separator/>"
+ " <toolitem action='AddToFav'/>"
+ " <toolitem action='DnBack'/>"
+ " <toolitem action='DnForward'/>"
+ " </toolbar>"
+ "</ui>";
+
+static GtkActionGroup *
+ldap_entries_page_page_get_actions_group (BrowserPage *page)
+{
+ LdapEntriesPage *ebrowser;
+
+ ebrowser = LDAP_ENTRIES_PAGE (page);
+ if (! ebrowser->priv->agroup) {
+ GtkActionGroup *agroup;
+ agroup = gtk_action_group_new ("LdapLdapEntriesPageActions");
+ gtk_action_group_set_translation_domain (agroup, GETTEXT_PACKAGE);
+ gtk_action_group_add_actions (agroup, ui_actions, G_N_ELEMENTS (ui_actions), page);
+ ebrowser->priv->agroup = agroup;
+
+ GtkAction *action;
+ const gchar *current_dn;
+ action = gtk_action_group_get_action (agroup, "AddToFav");
+ current_dn = ldap_entries_page_get_current_dn (ebrowser);
+ gtk_action_set_sensitive (action, (current_dn && *current_dn) ? TRUE : FALSE);
+
+ update_history_actions (ebrowser);
+ }
+
+ return g_object_ref (ebrowser->priv->agroup);
+}
+
+static const gchar *
+ldap_entries_page_page_get_actions_ui (G_GNUC_UNUSED BrowserPage *page)
+{
+ return ui_actions_browser;
+}
+
+static GtkWidget *
+ldap_entries_page_page_get_tab_label (BrowserPage *page, GtkWidget **out_close_button)
+{
+ LdapEntriesPage *ebrowser;
+ const gchar *tab_name;
+ GdkPixbuf *entries_pixbuf;
+
+ ebrowser = LDAP_ENTRIES_PAGE (page);
+ entries_pixbuf = browser_get_pixbuf_icon (BROWSER_ICON_LDAP_ORGANIZATION);
+ tab_name = _("LDAP entries");
+ return browser_make_tab_label_with_pixbuf (tab_name,
+ entries_pixbuf,
+ out_close_button ? TRUE : FALSE, out_close_button);
+}
diff --git a/tools/browser/ldap-browser/ldap-entries-page.h b/tools/browser/ldap-browser/ldap-entries-page.h
new file mode 100644
index 0000000..6d3c46e
--- /dev/null
+++ b/tools/browser/ldap-browser/ldap-entries-page.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2011 The GNOME Foundation
+ *
+ * AUTHORS:
+ * Vivien Malerba <malerba gnome-db org>
+ *
+ * This Program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __LDAP_ENTRIES_PAGE_H__
+#define __LDAP_ENTRIES_PAGE_H__
+
+#include <gtk/gtk.h>
+#include "../browser-connection.h"
+
+G_BEGIN_DECLS
+
+#define LDAP_ENTRIES_PAGE_TYPE (ldap_entries_page_get_type())
+#define LDAP_ENTRIES_PAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, LDAP_ENTRIES_PAGE_TYPE, LdapEntriesPage))
+#define LDAP_ENTRIES_PAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, LDAP_ENTRIES_PAGE_TYPE, LdapEntriesPageClass))
+#define IS_LDAP_ENTRIES_PAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, LDAP_ENTRIES_PAGE_TYPE))
+#define IS_LDAP_ENTRIES_PAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), LDAP_ENTRIES_PAGE_TYPE))
+
+typedef struct _LdapEntriesPage LdapEntriesPage;
+typedef struct _LdapEntriesPageClass LdapEntriesPageClass;
+typedef struct _LdapEntriesPagePrivate LdapEntriesPagePrivate;
+
+struct _LdapEntriesPage {
+ GtkVBox parent;
+ LdapEntriesPagePrivate *priv;
+};
+
+struct _LdapEntriesPageClass {
+ GtkVBoxClass parent_class;
+};
+
+GType ldap_entries_page_get_type (void) G_GNUC_CONST;
+
+GtkWidget *ldap_entries_page_new (BrowserConnection *bcnc, const gchar *dn);
+const gchar *ldap_entries_page_get_current_dn (LdapEntriesPage *ldap_entries_page);
+void ldap_entries_page_set_current_dn (LdapEntriesPage *ldap_entries_page, const gchar *dn);
+
+G_END_DECLS
+
+#endif
diff --git a/tools/browser/ldap-browser/ldap-favorite-selector.c b/tools/browser/ldap-browser/ldap-favorite-selector.c
new file mode 100644
index 0000000..33bd736
--- /dev/null
+++ b/tools/browser/ldap-browser/ldap-favorite-selector.c
@@ -0,0 +1,657 @@
+/*
+ * Copyright (C) 2011 The GNOME Foundation
+ *
+ * AUTHORS:
+ * Vivien Malerba <malerba gnome-db org>
+ *
+ * This Program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <glib/gi18n-lib.h>
+#include <string.h>
+#include <gtk/gtk.h>
+#include <libgda/gda-tree.h>
+#include "ldap-favorite-selector.h"
+#include "../mgr-favorites.h"
+#include <libgda-ui/gdaui-tree-store.h>
+#include "../dnd.h"
+#include "../support.h"
+#include "marshal.h"
+#include "../cc-gray-bar.h"
+#include "../browser-favorites.h"
+#include <gdk/gdkkeysyms.h>
+#include <libgda-ui/internal/popup-container.h>
+
+struct _LdapFavoriteSelectorPrivate {
+ BrowserConnection *bcnc;
+ GdaTree *tree;
+ GtkWidget *treeview;
+ guint idle_update_favorites;
+
+ GtkWidget *popup_menu;
+ GtkWidget *popup_properties;
+ GtkWidget *properties_name;
+ GtkWidget *properties_descr;
+ gint properties_id;
+ gint properties_position;
+ guint prop_save_timeout;
+};
+
+static void ldap_favorite_selector_class_init (LdapFavoriteSelectorClass *klass);
+static void ldap_favorite_selector_init (LdapFavoriteSelector *fsel,
+ LdapFavoriteSelectorClass *klass);
+static void ldap_favorite_selector_dispose (GObject *object);
+
+static void favorites_changed_cb (BrowserFavorites *bfav, LdapFavoriteSelector *fsel);
+
+enum {
+ SELECTION_CHANGED,
+ LAST_SIGNAL
+};
+
+static guint ldap_favorite_selector_signals[LAST_SIGNAL] = { 0 };
+static GObjectClass *parent_class = NULL;
+
+/* columns of the resulting GtkTreeModel */
+enum {
+ COLUMN_ID = 0,
+ COLUMN_NAME = 1,
+ COLUMN_ICON = 2,
+ COLUMN_MARKUP = 3,
+ COLUMN_POSITION = 4,
+ COLUMN_DESCR = 5,
+ COLUMN_FAVTYPE = 6,
+ COLUMN_LAST
+};
+
+
+/*
+ * LdapFavoriteSelector class implementation
+ */
+
+static void
+ldap_favorite_selector_class_init (LdapFavoriteSelectorClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ /* signals */
+ ldap_favorite_selector_signals [SELECTION_CHANGED] =
+ g_signal_new ("selection-changed",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (LdapFavoriteSelectorClass, selection_changed),
+ NULL, NULL,
+ _ldap_marshal_VOID__INT_ENUM_STRING, G_TYPE_NONE,
+ 3, G_TYPE_INT, G_TYPE_UINT, G_TYPE_STRING);
+ klass->selection_changed = NULL;
+
+ object_class->dispose = ldap_favorite_selector_dispose;
+}
+
+
+static void
+ldap_favorite_selector_init (LdapFavoriteSelector *fsel, G_GNUC_UNUSED LdapFavoriteSelectorClass *klass)
+{
+ fsel->priv = g_new0 (LdapFavoriteSelectorPrivate, 1);
+ fsel->priv->idle_update_favorites = 0;
+ fsel->priv->prop_save_timeout = 0;
+}
+
+static void
+ldap_favorite_selector_dispose (GObject *object)
+{
+ LdapFavoriteSelector *fsel = (LdapFavoriteSelector *) object;
+
+ /* free memory */
+ if (fsel->priv) {
+ if (fsel->priv->idle_update_favorites != 0)
+ g_source_remove (fsel->priv->idle_update_favorites);
+ if (fsel->priv->prop_save_timeout)
+ g_source_remove (fsel->priv->prop_save_timeout);
+
+ if (fsel->priv->tree)
+ g_object_unref (fsel->priv->tree);
+
+ if (fsel->priv->bcnc) {
+ g_signal_handlers_disconnect_by_func (browser_connection_get_favorites (fsel->priv->bcnc),
+ G_CALLBACK (favorites_changed_cb), fsel);
+ g_object_unref (fsel->priv->bcnc);
+ }
+
+ if (fsel->priv->popup_properties)
+ gtk_widget_destroy (fsel->priv->popup_properties);
+ if (fsel->priv->popup_menu)
+ gtk_widget_destroy (fsel->priv->popup_menu);
+
+ g_free (fsel->priv);
+ fsel->priv = NULL;
+ }
+
+ parent_class->dispose (object);
+}
+
+GType
+ldap_favorite_selector_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0)) {
+ static const GTypeInfo info = {
+ sizeof (LdapFavoriteSelectorClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) ldap_favorite_selector_class_init,
+ NULL,
+ NULL,
+ sizeof (LdapFavoriteSelector),
+ 0,
+ (GInstanceInitFunc) ldap_favorite_selector_init,
+ 0
+ };
+ type = g_type_register_static (GTK_TYPE_VBOX, "LdapFavoriteSelector",
+ &info, 0);
+ }
+ return type;
+}
+
+static void
+favorite_delete_selected (LdapFavoriteSelector *fsel)
+{
+ GtkTreeModel *model;
+ GtkTreeSelection *select;
+ GtkTreeIter iter;
+
+ select = gtk_tree_view_get_selection (GTK_TREE_VIEW (fsel->priv->treeview));
+ if (gtk_tree_selection_get_selected (select, &model, &iter)) {
+ BrowserFavorites *bfav;
+ BrowserFavoritesAttributes fav;
+ GError *lerror = NULL;
+
+ memset (&fav, 0, sizeof (BrowserFavoritesAttributes));
+ gtk_tree_model_get (model, &iter,
+ COLUMN_ID, &(fav.id), -1);
+ bfav = browser_connection_get_favorites (fsel->priv->bcnc);
+ if (!browser_favorites_delete (bfav, 0, &fav, NULL)) {
+ browser_show_error ((GtkWindow*) gtk_widget_get_toplevel ((GtkWidget*)fsel),
+ _("Could not remove favorite: %s"),
+ lerror && lerror->message ? lerror->message : _("No detail"));
+ if (lerror)
+ g_error_free (lerror);
+ }
+ }
+}
+
+static gboolean
+key_press_event_cb (GtkTreeView *treeview, GdkEventKey *event, LdapFavoriteSelector *fsel)
+{
+ if (event->keyval == GDK_KEY_Delete) {
+ favorite_delete_selected (fsel);
+ return TRUE;
+ }
+ return FALSE; /* not handled */
+}
+
+
+static void
+selection_changed_cb (GtkTreeView *treeview, G_GNUC_UNUSED GtkTreePath *path,
+ G_GNUC_UNUSED GtkTreeViewColumn *column, LdapFavoriteSelector *fsel)
+{
+ GtkTreeModel *model;
+ GtkTreeSelection *select;
+ GtkTreeIter iter;
+
+ select = gtk_tree_view_get_selection (treeview);
+ if (gtk_tree_selection_get_selected (select, &model, &iter)) {
+ gchar *str;
+ gint fav_id;
+ BrowserFavoritesType fav_type;
+ gtk_tree_model_get (model, &iter,
+ COLUMN_ID, &fav_id,
+ COLUMN_FAVTYPE, &fav_type,
+ COLUMN_NAME, &str, -1);
+ g_signal_emit (fsel, ldap_favorite_selector_signals [SELECTION_CHANGED], 0, fav_id,
+ fav_type, str);
+ g_free (str);
+ }
+}
+
+static gboolean
+prop_save_timeout (LdapFavoriteSelector *fsel)
+{
+ BrowserFavorites *bfav;
+ BrowserFavoritesAttributes fav;
+ GError *error = NULL;
+ gboolean allok;
+
+ bfav = browser_connection_get_favorites (fsel->priv->bcnc);
+
+ memset (&fav, 0, sizeof (BrowserFavoritesAttributes));
+ fav.id = fsel->priv->properties_id;
+ fav.type = BROWSER_FAVORITES_LDAP_DN;
+ fav.name = (gchar*) gtk_entry_get_text (GTK_ENTRY (fsel->priv->properties_name));
+ fav.descr = (gchar*) gtk_entry_get_text (GTK_ENTRY (fsel->priv->properties_descr));
+ fav.contents = (gchar*) gtk_entry_get_text (GTK_ENTRY (fsel->priv->properties_name));
+
+ allok = browser_favorites_add (bfav, 0, &fav, ORDER_KEY_LDAP,
+ fsel->priv->properties_position, &error);
+ if (! allok) {
+ browser_show_error ((GtkWindow*) gtk_widget_get_toplevel ((GtkWidget*) fsel),
+ _("Could not add favorite: %s"),
+ error && error->message ? error->message : _("No detail"));
+ if (error)
+ g_error_free (error);
+ }
+
+ fsel->priv->prop_save_timeout = 0;
+ return FALSE; /* remove timeout */
+}
+
+static void
+property_changed_cb (G_GNUC_UNUSED GtkWidget *multiple, LdapFavoriteSelector *fsel)
+{
+ if (fsel->priv->prop_save_timeout)
+ g_source_remove (fsel->priv->prop_save_timeout);
+ fsel->priv->prop_save_timeout = g_timeout_add (200, (GSourceFunc) prop_save_timeout, fsel);
+}
+
+static void
+properties_activated_cb (GtkMenuItem *mitem, LdapFavoriteSelector *fsel)
+{
+ if (! fsel->priv->popup_properties) {
+ GtkWidget *pcont, *vbox, *hbox, *label, *entry, *table;
+ gchar *str;
+ gfloat align;
+
+ pcont = popup_container_new (GTK_WIDGET (mitem));
+ vbox = gtk_vbox_new (FALSE, 0);
+ gtk_container_add (GTK_CONTAINER (pcont), vbox);
+
+ label = gtk_label_new ("");
+ str = g_strdup_printf ("<b>%s:</b>", _("Favorite's properties"));
+ gtk_label_set_markup (GTK_LABEL (label), str);
+ g_free (str);
+ gtk_misc_get_alignment (GTK_MISC (label), NULL, &align);
+ gtk_misc_set_alignment (GTK_MISC (label), 0., align);
+ gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+
+ hbox = gtk_hbox_new (FALSE, 0); /* HIG */
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 5);
+ label = gtk_label_new (" ");
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+
+ table = gtk_table_new (2, 2, FALSE);
+ gtk_box_pack_start (GTK_BOX (hbox), table, TRUE, TRUE, 0);
+
+ label = gtk_label_new ("");
+ str = g_strdup_printf ("<b>%s:</b>", _("Name"));
+ gtk_label_set_markup (GTK_LABEL (label), str);
+ g_free (str);
+ gtk_misc_get_alignment (GTK_MISC (label), NULL, &align);
+ gtk_misc_set_alignment (GTK_MISC (label), 0., align);
+ gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1, GTK_FILL, GTK_FILL, 0, 0);
+
+ label = gtk_label_new ("");
+ str = g_strdup_printf ("<b>%s:</b>", _("Description"));
+ gtk_label_set_markup (GTK_LABEL (label), str);
+ g_free (str);
+ gtk_misc_set_alignment (GTK_MISC (label), 0., 0.);
+ gtk_table_attach (GTK_TABLE (table), label, 0, 1, 1, 2, GTK_FILL, GTK_FILL, 0, 0);
+
+ entry = gtk_entry_new ();
+ gtk_editable_set_editable (GTK_EDITABLE (entry), FALSE);
+ gtk_table_attach (GTK_TABLE (table), entry, 1, 2, 0, 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
+ fsel->priv->properties_name = entry;
+
+ entry = gtk_entry_new ();
+ gtk_widget_set_size_request (entry, 200, -1);
+ gtk_table_attach (GTK_TABLE (table), entry, 1, 2, 1, 2, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
+ fsel->priv->properties_descr = entry;
+ g_signal_connect (entry, "changed",
+ G_CALLBACK (property_changed_cb), fsel);
+
+ fsel->priv->popup_properties = pcont;
+ gtk_widget_show_all (vbox);
+ }
+
+ /* adjust contents */
+ GtkTreeSelection *selection;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (fsel->priv->treeview));
+ if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
+ gchar *name, *descr;
+
+ gtk_tree_model_get (model, &iter,
+ COLUMN_ID, &(fsel->priv->properties_id),
+ COLUMN_POSITION, &(fsel->priv->properties_position),
+ COLUMN_NAME, &name,
+ COLUMN_DESCR, &descr, -1);
+
+ if (name) {
+ gtk_entry_set_text (GTK_ENTRY (fsel->priv->properties_name), name);
+ g_free (name);
+ }
+
+ g_signal_handlers_block_by_func (fsel->priv->properties_descr,
+ G_CALLBACK (property_changed_cb), fsel);
+ gtk_entry_set_text (GTK_ENTRY (fsel->priv->properties_descr), descr ? descr : "");
+ g_signal_handlers_unblock_by_func (fsel->priv->properties_descr,
+ G_CALLBACK (property_changed_cb), fsel);
+ g_free (descr);
+
+ gtk_widget_show (fsel->priv->popup_properties);
+ }
+}
+
+static void
+delete_activated_cb (G_GNUC_UNUSED GtkMenuItem *mitem, LdapFavoriteSelector *fsel)
+{
+ favorite_delete_selected (fsel);
+}
+
+static void
+do_popup_menu (G_GNUC_UNUSED GtkWidget *widget, GdkEventButton *event, LdapFavoriteSelector *fsel)
+{
+ int button, event_time;
+
+ if (! fsel->priv->popup_menu) {
+ GtkWidget *menu, *mitem;
+
+ menu = gtk_menu_new ();
+ g_signal_connect (menu, "deactivate",
+ G_CALLBACK (gtk_widget_hide), NULL);
+
+ mitem = gtk_image_menu_item_new_from_stock (GTK_STOCK_PROPERTIES, NULL);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), mitem);
+ gtk_widget_show (mitem);
+ g_signal_connect (mitem, "activate",
+ G_CALLBACK (properties_activated_cb), fsel);
+
+ mitem = gtk_image_menu_item_new_from_stock (GTK_STOCK_DELETE, NULL);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), mitem);
+ gtk_widget_show (mitem);
+ g_signal_connect (mitem, "activate",
+ G_CALLBACK (delete_activated_cb), fsel);
+
+ fsel->priv->popup_menu = menu;
+ }
+
+ if (event) {
+ button = event->button;
+ event_time = event->time;
+ }
+ else {
+ button = 0;
+ event_time = gtk_get_current_event_time ();
+ }
+
+ gtk_menu_popup (GTK_MENU (fsel->priv->popup_menu), NULL, NULL, NULL, NULL,
+ button, event_time);
+}
+
+
+static gboolean
+popup_menu_cb (GtkWidget *widget, LdapFavoriteSelector *fsel)
+{
+ do_popup_menu (widget, NULL, fsel);
+ return TRUE;
+}
+
+static gboolean
+button_press_event_cb (GtkTreeView *treeview, GdkEventButton *event, LdapFavoriteSelector *fsel)
+{
+ if (event->button == 3 && event->type == GDK_BUTTON_PRESS) {
+ do_popup_menu ((GtkWidget*) treeview, event, fsel);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean idle_update_favorites (LdapFavoriteSelector *fsel);
+static gboolean tree_store_drag_drop_cb (GdauiTreeStore *store, const gchar *path,
+ GtkSelectionData *selection_ldap, LdapFavoriteSelector *fsel);
+static gboolean tree_store_drag_can_drag_cb (GdauiTreeStore *store, const gchar *path,
+ LdapFavoriteSelector *fsel);
+static gboolean tree_store_drag_get_cb (GdauiTreeStore *store, const gchar *path,
+ GtkSelectionData *selection_ldap, LdapFavoriteSelector *fsel);
+
+/**
+ * ldap_favorite_selector_new
+ *
+ * Returns: a new #GtkWidget
+ */
+GtkWidget *
+ldap_favorite_selector_new (BrowserConnection *bcnc)
+{
+ LdapFavoriteSelector *fsel;
+ GdaTreeManager *manager;
+ gchar *signame;
+
+ g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), NULL);
+ fsel = LDAP_FAVORITE_SELECTOR (g_object_new (LDAP_FAVORITE_SELECTOR_TYPE, NULL));
+
+ fsel->priv->bcnc = g_object_ref (bcnc);
+ signame = g_strdup_printf ("favorites-changed::%s",
+ browser_favorites_type_to_string (BROWSER_FAVORITES_LDAP_DN));
+ g_signal_connect (browser_connection_get_favorites (fsel->priv->bcnc), signame,
+ G_CALLBACK (favorites_changed_cb), fsel);
+ g_free (signame);
+ signame = g_strdup_printf ("favorites-changed::%s",
+ browser_favorites_type_to_string (BROWSER_FAVORITES_LDAP_CLASS));
+ g_signal_connect (browser_connection_get_favorites (fsel->priv->bcnc), signame,
+ G_CALLBACK (favorites_changed_cb), fsel);
+ g_free (signame);
+
+ /* create tree managers */
+ fsel->priv->tree = gda_tree_new ();
+ manager = mgr_favorites_new (bcnc, BROWSER_FAVORITES_LDAP_DN, ORDER_KEY_LDAP);
+ gda_tree_add_manager (fsel->priv->tree, manager);
+ g_object_unref (manager);
+ manager = mgr_favorites_new (bcnc, BROWSER_FAVORITES_LDAP_CLASS, ORDER_KEY_LDAP);
+ gda_tree_add_manager (fsel->priv->tree, manager);
+ g_object_unref (manager);
+
+ /* update the tree's contents */
+ if (! gda_tree_update_all (fsel->priv->tree, NULL)) {
+ if (fsel->priv->idle_update_favorites == 0)
+ fsel->priv->idle_update_favorites = g_idle_add ((GSourceFunc) idle_update_favorites, fsel);
+ }
+
+ /* header */
+ GtkWidget *label;
+ gchar *str;
+ str = g_strdup_printf ("<b>%s</b>", _("Favorites"));
+ label = cc_gray_bar_new (str);
+ g_free (str);
+ cc_gray_bar_set_icon_from_pixbuf (CC_GRAY_BAR (label), browser_get_pixbuf_icon (BROWSER_ICON_BOOKMARK));
+ gtk_box_pack_start (GTK_BOX (fsel), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ /* tree model & tree view */
+ GtkTreeModel *model;
+ GtkWidget *treeview;
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+
+ model = gdaui_tree_store_new (fsel->priv->tree, COLUMN_LAST,
+ G_TYPE_INT, MGR_FAVORITES_ID_ATT_NAME,
+ G_TYPE_STRING, MGR_FAVORITES_CONTENTS_ATT_NAME,
+ G_TYPE_OBJECT, "icon",
+ G_TYPE_STRING, "markup",
+ G_TYPE_INT, MGR_FAVORITES_ID_ATT_NAME,
+ G_TYPE_STRING, "descr",
+ G_TYPE_UINT, MGR_FAVORITES_TYPE_ATT_NAME);
+
+ treeview = browser_make_tree_view (model);
+ fsel->priv->treeview = treeview;
+ g_object_unref (model);
+
+ g_signal_connect (G_OBJECT (treeview), "row-activated",
+ G_CALLBACK (selection_changed_cb), fsel);
+ g_signal_connect (G_OBJECT (treeview), "key-press-event",
+ G_CALLBACK (key_press_event_cb), fsel);
+ g_signal_connect (G_OBJECT (treeview), "popup-menu",
+ G_CALLBACK (popup_menu_cb), fsel);
+ g_signal_connect (G_OBJECT (treeview), "button-press-event",
+ G_CALLBACK (button_press_event_cb), fsel);
+
+ /* icon */
+ column = gtk_tree_view_column_new ();
+
+ renderer = gtk_cell_renderer_pixbuf_new ();
+ gtk_tree_view_column_pack_start (column, renderer, FALSE);
+ gtk_tree_view_column_add_attribute (column, renderer, "pixbuf", COLUMN_ICON);
+ g_object_set ((GObject*) renderer, "yalign", 0., NULL);
+
+ /* text */
+ renderer = gtk_cell_renderer_text_new ();
+ gtk_tree_view_column_pack_start (column, renderer, TRUE);
+ gtk_tree_view_column_add_attribute (column, renderer, "markup", COLUMN_MARKUP);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
+ gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
+
+ /* scrolled window packing */
+ GtkWidget *sw;
+ sw = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw),
+ GTK_SHADOW_ETCHED_IN);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
+ GTK_POLICY_NEVER,
+ GTK_POLICY_AUTOMATIC);
+ gtk_container_add (GTK_CONTAINER (sw), treeview);
+ gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
+
+ gtk_box_pack_start (GTK_BOX (fsel), sw, TRUE, TRUE, 0);
+ gtk_widget_show_all (sw);
+
+ /* DnD */
+ gtk_tree_view_enable_model_drag_dest (GTK_TREE_VIEW (treeview), dbo_table, G_N_ELEMENTS (dbo_table),
+ GDK_ACTION_COPY);
+ gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (treeview), GDK_BUTTON1_MASK,
+ dbo_table, G_N_ELEMENTS (dbo_table),
+ GDK_ACTION_COPY | GDK_ACTION_MOVE);
+ g_signal_connect (model, "drag-drop",
+ G_CALLBACK (tree_store_drag_drop_cb), fsel);
+ g_signal_connect (model, "drag-can-drag",
+ G_CALLBACK (tree_store_drag_can_drag_cb), fsel);
+ g_signal_connect (model, "drag-get",
+ G_CALLBACK (tree_store_drag_get_cb), fsel);
+
+ return (GtkWidget*) fsel;
+}
+
+static gboolean
+idle_update_favorites (LdapFavoriteSelector *fsel)
+{
+ gboolean done;
+ g_print ("%s()\n", __FUNCTION__);
+ done = gda_tree_update_all (fsel->priv->tree, NULL);
+ if (done)
+ fsel->priv->idle_update_favorites = 0;
+ else
+ fsel->priv->idle_update_favorites = g_timeout_add_seconds (1, (GSourceFunc) idle_update_favorites,
+ fsel);
+ return FALSE;
+}
+
+static gboolean
+tree_store_drag_drop_cb (G_GNUC_UNUSED GdauiTreeStore *store, const gchar *path,
+ GtkSelectionData *selection_ldap, LdapFavoriteSelector *fsel)
+{
+ BrowserFavorites *bfav;
+ BrowserFavoritesAttributes fav;
+ GError *error = NULL;
+ gint pos;
+ gboolean retval = TRUE;
+ gint id;
+ bfav = browser_connection_get_favorites (fsel->priv->bcnc);
+
+ id = browser_favorites_find (bfav, 0, (gchar*) gtk_selection_data_get_data (selection_ldap),
+ &fav, NULL);
+ if (id < 0) {
+ memset (&fav, 0, sizeof (BrowserFavoritesAttributes));
+ fav.id = -1;
+ fav.type = BROWSER_FAVORITES_LDAP_DN;
+ fav.name = (gchar*) gtk_selection_data_get_data (selection_ldap);
+ fav.descr = NULL;
+ fav.contents = (gchar*) gtk_selection_data_get_data (selection_ldap);
+ }
+
+ pos = atoi (path);
+ /*g_print ("%s() path => %s, pos: %d\n", __FUNCTION__, path, pos);*/
+
+ if (! browser_favorites_add (bfav, 0, &fav, ORDER_KEY_LDAP, pos, &error)) {
+ browser_show_error ((GtkWindow*) gtk_widget_get_toplevel ((GtkWidget*) fsel),
+ _("Could not add favorite: %s"),
+ error && error->message ? error->message : _("No detail"));
+ if (error)
+ g_error_free (error);
+ retval = FALSE;
+ }
+
+ if (id >= 0)
+ browser_favorites_reset_attributes (&fav);
+
+ return retval;
+}
+
+static gboolean
+tree_store_drag_can_drag_cb (G_GNUC_UNUSED GdauiTreeStore *store, const gchar *path,
+ LdapFavoriteSelector *fsel)
+{
+ GdaTreeNode *node;
+ node = gda_tree_get_node (fsel->priv->tree, path, FALSE);
+ if (node) {
+ const GValue *cvalue;
+ cvalue = gda_tree_node_get_node_attribute (node, "fav_contents");
+ if (cvalue)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static gboolean
+tree_store_drag_get_cb (G_GNUC_UNUSED GdauiTreeStore *store, const gchar *path,
+ GtkSelectionData *selection_ldap, LdapFavoriteSelector *fsel)
+{
+ GdaTreeNode *node;
+ node = gda_tree_get_node (fsel->priv->tree, path, FALSE);
+ if (node) {
+ const GValue *cvalue;
+ cvalue = gda_tree_node_get_node_attribute (node, "fav_contents");
+ if (cvalue) {
+ const gchar *str;
+ str = g_value_get_string (cvalue);
+ gtk_selection_data_set (selection_ldap, gtk_selection_data_get_target (selection_ldap),
+ 8, (guchar*) str, strlen (str));
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+static void
+favorites_changed_cb (G_GNUC_UNUSED BrowserFavorites *bfav, LdapFavoriteSelector *fsel)
+{
+ if (! gda_tree_update_all (fsel->priv->tree, NULL)) {
+ if (fsel->priv->idle_update_favorites == 0)
+ fsel->priv->idle_update_favorites = g_idle_add ((GSourceFunc) idle_update_favorites, fsel);
+
+ }
+}
diff --git a/tools/browser/ldap-browser/ldap-favorite-selector.h b/tools/browser/ldap-browser/ldap-favorite-selector.h
new file mode 100644
index 0000000..2539f94
--- /dev/null
+++ b/tools/browser/ldap-browser/ldap-favorite-selector.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2011 The GNOME Foundation
+ *
+ * AUTHORS:
+ * Vivien Malerba <malerba gnome-db org>
+ *
+ * This Program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __LDAP_FAVORITE_SELECTOR_H__
+#define __LDAP_FAVORITE_SELECTOR_H__
+
+#include <gtk/gtk.h>
+#include "../browser-connection.h"
+
+G_BEGIN_DECLS
+
+#define LDAP_FAVORITE_SELECTOR_TYPE (ldap_favorite_selector_get_type())
+#define LDAP_FAVORITE_SELECTOR(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, LDAP_FAVORITE_SELECTOR_TYPE, LdapFavoriteSelector))
+#define LDAP_FAVORITE_SELECTOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, LDAP_FAVORITE_SELECTOR_TYPE, LdapFavoriteSelectorClass))
+#define IS_LDAP_FAVORITE_SELECTOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, LDAP_FAVORITE_SELECTOR_TYPE))
+#define IS_LDAP_FAVORITE_SELECTOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), LDAP_FAVORITE_SELECTOR_TYPE))
+
+typedef struct _LdapFavoriteSelector LdapFavoriteSelector;
+typedef struct _LdapFavoriteSelectorClass LdapFavoriteSelectorClass;
+typedef struct _LdapFavoriteSelectorPrivate LdapFavoriteSelectorPrivate;
+
+struct _LdapFavoriteSelector {
+ GtkVBox parent;
+ LdapFavoriteSelectorPrivate *priv;
+};
+
+struct _LdapFavoriteSelectorClass {
+ GtkVBoxClass parent_class;
+
+ void (*selection_changed) (LdapFavoriteSelector *sel, gint fav_id,
+ BrowserFavoritesType fav_type, const gchar *fav_contents);
+};
+
+GType ldap_favorite_selector_get_type (void) G_GNUC_CONST;
+
+GtkWidget *ldap_favorite_selector_new (BrowserConnection *bcnc);
+
+G_END_DECLS
+
+#endif
diff --git a/tools/browser/ldap-browser/ldap-search-page.c b/tools/browser/ldap-browser/ldap-search-page.c
new file mode 100644
index 0000000..6f18198
--- /dev/null
+++ b/tools/browser/ldap-browser/ldap-search-page.c
@@ -0,0 +1,538 @@
+/*
+ * Copyright (C) 2011 The GNOME Foundation
+ *
+ * AUTHORS:
+ * Vivien Malerba <malerba gnome-db org>
+ *
+ * This Program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <glib/gi18n-lib.h>
+#include <string.h>
+#include "ldap-search-page.h"
+#include "filter-editor.h"
+#include "../cc-gray-bar.h"
+#include "../browser-page.h"
+#include "../browser-window.h"
+#include "../browser-connection.h"
+#include <virtual/gda-ldap-connection.h>
+#include <common/ui-formgrid.h>
+#include "../browser-stock-icons.h"
+#include "vtable-dialog.h"
+
+typedef struct {
+ gchar *base_dn;
+ gchar *filter;
+ gchar *attributes;
+ GdaLdapSearchScope scope;
+ GdaDataModel *result;
+} HistoryItem;
+
+static void
+history_item_free (HistoryItem *item)
+{
+ g_free (item->base_dn);
+ g_free (item->filter);
+ g_free (item->attributes);
+ if (item->result)
+ g_object_unref (item->result);
+ g_free (item);
+}
+
+struct _LdapSearchPagePrivate {
+ BrowserConnection *bcnc;
+
+ GtkWidget *search_entry;
+ GtkWidget *result_view;
+
+ GtkActionGroup *agroup;
+ GArray *history_items; /* array of @HistoryItem */
+ guint history_max_len; /* max allowed length of @history_items */
+ gint current_hitem; /* index in @history_items, or -1 */
+ gboolean add_hist_item;
+};
+
+static void ldap_search_page_class_init (LdapSearchPageClass *klass);
+static void ldap_search_page_init (LdapSearchPage *epage, LdapSearchPageClass *klass);
+static void ldap_search_page_dispose (GObject *object);
+
+/* BrowserPage interface */
+static void ldap_search_page_page_init (BrowserPageIface *iface);
+static GtkActionGroup *ldap_search_page_page_get_actions_group (BrowserPage *page);
+static const gchar *ldap_search_page_page_get_actions_ui (BrowserPage *page);
+static GtkWidget *ldap_search_page_page_get_tab_label (BrowserPage *page, GtkWidget **out_close_button);
+
+static GObjectClass *parent_class = NULL;
+
+
+/*
+ * LdapSearchPage class implementation
+ */
+
+static void
+ldap_search_page_class_init (LdapSearchPageClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ object_class->dispose = ldap_search_page_dispose;
+}
+
+static void
+ldap_search_page_page_init (BrowserPageIface *iface)
+{
+ iface->i_get_actions_group = ldap_search_page_page_get_actions_group;
+ iface->i_get_actions_ui = ldap_search_page_page_get_actions_ui;
+ iface->i_get_tab_label = ldap_search_page_page_get_tab_label;
+}
+
+static void
+ldap_search_page_init (LdapSearchPage *epage, G_GNUC_UNUSED LdapSearchPageClass *klass)
+{
+ epage->priv = g_new0 (LdapSearchPagePrivate, 1);
+ epage->priv->history_items = g_array_new (FALSE, FALSE, sizeof (HistoryItem*));
+ epage->priv->history_max_len = 20;
+ epage->priv->add_hist_item = TRUE;
+}
+
+static void
+ldap_search_page_dispose (GObject *object)
+{
+ LdapSearchPage *epage = (LdapSearchPage *) object;
+
+ /* free memory */
+ if (epage->priv) {
+ if (epage->priv->bcnc)
+ g_object_unref (epage->priv->bcnc);
+ if (epage->priv->agroup)
+ g_object_unref (epage->priv->agroup);
+ if (epage->priv->history_items) {
+ guint i;
+ for (i = 0; i < epage->priv->history_items->len; i++) {
+ HistoryItem *hitem = g_array_index (epage->priv->history_items,
+ HistoryItem*, i);
+ history_item_free (hitem);
+ }
+ g_array_free (epage->priv->history_items, TRUE);
+ }
+ g_free (epage->priv);
+ epage->priv = NULL;
+ }
+
+ parent_class->dispose (object);
+}
+
+GType
+ldap_search_page_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0)) {
+ static const GTypeInfo info = {
+ sizeof (LdapSearchPageClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) ldap_search_page_class_init,
+ NULL,
+ NULL,
+ sizeof (LdapSearchPage),
+ 0,
+ (GInstanceInitFunc) ldap_search_page_init,
+ 0
+ };
+
+ static GInterfaceInfo page_info = {
+ (GInterfaceInitFunc) ldap_search_page_page_init,
+ NULL,
+ NULL
+ };
+
+ type = g_type_register_static (GTK_TYPE_VBOX, "LdapSearchPage", &info, 0);
+ g_type_add_interface_static (type, BROWSER_PAGE_TYPE, &page_info);
+ }
+ return type;
+}
+
+static void
+update_history_actions (LdapSearchPage *epage)
+{
+ if (!epage->priv->agroup)
+ return;
+ /*
+ gboolean is_first = TRUE;
+ gboolean is_last = TRUE;
+ const gchar *current_classname;
+ epage->priv->current_hitem = -1;
+ current_classname = ldap_search_page_get_current_class (epage);
+ if (current_classname) {
+ guint i;
+ for (i = 0; i < epage->priv->history_items->len; i++) {
+ HistoryItem *hitem;
+ hitem = g_array_index (epage->priv->history_items, HistoryItem*, i);
+ if (!strcmp (hitem->classname, current_classname)) {
+ if (i != 0)
+ is_first = FALSE;
+ if (i+1 < epage->priv->history_items->len)
+ is_last = FALSE;
+ epage->priv->current_hitem = (gint) i;
+ break;
+ }
+ }
+ }
+
+ GtkAction *action;
+ action = gtk_action_group_get_action (epage->priv->agroup, "DnBack");
+ gtk_action_set_sensitive (action, !is_first);
+ action = gtk_action_group_get_action (epage->priv->agroup, "DnForward");
+ gtk_action_set_sensitive (action, !is_last);
+ */
+}
+
+static void
+search_done_cb (G_GNUC_UNUSED BrowserConnection *bcnc,
+ gpointer out_result, LdapSearchPage *epage, GError *error)
+{
+ if (epage->priv->result_view) {
+ gtk_widget_destroy (epage->priv->result_view);
+ epage->priv->result_view = NULL;
+ }
+ if (out_result == (gpointer) 0x01) {
+ TO_IMPLEMENT;
+ }
+ else {
+ GdaDataModel *model;
+ GtkWidget *wid;
+ model = GDA_DATA_MODEL (out_result);
+ wid = ui_formgrid_new (model, TRUE, GDAUI_DATA_PROXY_INFO_CURRENT_ROW);
+ g_object_unref (model);
+ gtk_box_pack_start (GTK_BOX (epage), wid, TRUE, TRUE, 0);
+ epage->priv->result_view = wid;
+ gtk_widget_show (wid);
+ }
+ g_object_unref (G_OBJECT (epage));
+}
+
+static void
+filter_exec_clicked_cb (GtkWidget *button, LdapSearchPage *epage)
+{
+ guint id;
+ gchar *base_dn, *filter, *attributes;
+ GdaLdapSearchScope scope;
+ GError *lerror = NULL;
+ filter_editor_get_settings (FILTER_EDITOR (epage->priv->search_entry),
+ &base_dn, &filter, &attributes, &scope);
+
+ id = browser_connection_ldap_search (epage->priv->bcnc,
+ base_dn, filter,
+ attributes, scope,
+ BROWSER_CONNECTION_JOB_CALLBACK (search_done_cb),
+ g_object_ref (G_OBJECT (epage)), &lerror);
+
+ if (id == 0) {
+ TO_IMPLEMENT;
+ g_clear_error (&lerror);
+ }
+
+ g_free (base_dn);
+ g_free (filter);
+ g_free (attributes);
+}
+
+static void
+filter_clear_clicked_cb (GtkWidget *button, LdapSearchPage *epage)
+{
+ filter_editor_clear (FILTER_EDITOR (epage->priv->search_entry));
+}
+
+/**
+ * ldap_search_page_new:
+ * @bcnc:
+ * @base_dn: the base DN to put in the search page by default
+ *
+ * Returns: a new #GtkWidget
+ */
+GtkWidget *
+ldap_search_page_new (BrowserConnection *bcnc, const gchar *base_dn)
+{
+ LdapSearchPage *epage;
+
+ g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), NULL);
+
+ epage = LDAP_SEARCH_PAGE (g_object_new (LDAP_SEARCH_PAGE_TYPE, NULL));
+ epage->priv->bcnc = g_object_ref ((GObject*) bcnc);
+
+ /* header */
+ GtkWidget *label;
+ gchar *str;
+
+ str = g_strdup_printf ("<b>%s</b>", _("LDAP search page"));
+ label = cc_gray_bar_new (str);
+ g_free (str);
+ gtk_box_pack_start (GTK_BOX (epage), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ /* search filter settings */
+ GtkWidget *hb, *bb, *button, *wid;
+ gchar *tmp;
+ gfloat ya;
+ tmp = g_markup_printf_escaped ("<b>%s:</b>", _("LDAP search settings"));
+ label = gtk_label_new ("");
+ gtk_label_set_markup (GTK_LABEL (label), tmp);
+ g_free (tmp);
+ gtk_misc_get_alignment (GTK_MISC (label), NULL, &ya);
+ gtk_misc_set_alignment (GTK_MISC (label), 0., ya);
+ gtk_box_pack_start (GTK_BOX (epage), label, FALSE, FALSE, 0);
+
+ hb = gtk_hbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (epage), hb, FALSE, FALSE, 3);
+
+ wid = filter_editor_new (bcnc);
+ if (!base_dn)
+ base_dn = browser_connection_ldap_get_base_dn (bcnc);
+ filter_editor_set_settings (FILTER_EDITOR (wid), base_dn, NULL, NULL,
+ GDA_LDAP_SEARCH_SUBTREE);
+ gtk_box_pack_start (GTK_BOX (hb), wid, TRUE, TRUE, 0);
+ epage->priv->search_entry = wid;
+
+ bb = gtk_vbutton_box_new ();
+ gtk_button_box_set_layout (GTK_BUTTON_BOX (bb), GTK_BUTTONBOX_END);
+ gtk_box_pack_start (GTK_BOX (hb), bb, FALSE, FALSE, 5);
+
+ button = browser_make_small_button (FALSE, FALSE, _("Clear"),
+ GTK_STOCK_CLEAR, _("Clear the search settings"));
+ gtk_box_pack_start (GTK_BOX (bb), button, TRUE, TRUE, 0);
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (filter_clear_clicked_cb), epage);
+
+ button = browser_make_small_button (FALSE, FALSE, _("Execute"),
+ GTK_STOCK_EXECUTE, _("Execute LDAP search"));
+ gtk_box_pack_start (GTK_BOX (bb), button, TRUE, TRUE, 0);
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (filter_exec_clicked_cb), epage);
+
+ /* results */
+ tmp = g_markup_printf_escaped ("<b>%s:</b>", _("Results"));
+ label = gtk_label_new ("");
+ gtk_label_set_markup (GTK_LABEL (label), tmp);
+ g_free (tmp);
+ gtk_misc_get_alignment (GTK_MISC (label), NULL, &ya);
+ gtk_misc_set_alignment (GTK_MISC (label), 0., ya);
+ gtk_box_pack_start (GTK_BOX (epage), label, FALSE, FALSE, 5);
+
+ wid = ui_formgrid_new (NULL, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (epage), wid, TRUE, TRUE, 0);
+ epage->priv->result_view = wid;
+
+ gtk_widget_show_all ((GtkWidget*) epage);
+ gtk_widget_hide ((GtkWidget*) epage);
+
+ return (GtkWidget*) epage;
+}
+
+/*
+ * UI actions
+ */
+static void
+action_define_as_table_cb (G_GNUC_UNUSED GtkAction *action, LdapSearchPage *epage)
+{
+ GtkWidget *dlg;
+ gint res;
+ GtkWindow *parent;
+
+ parent = (GtkWindow*) gtk_widget_get_toplevel ((GtkWidget*) epage);
+
+ dlg = vtable_dialog_new (parent, epage->priv->bcnc);
+ res = gtk_dialog_run (GTK_DIALOG (dlg));
+ gtk_widget_hide (dlg);
+ if (res == GTK_RESPONSE_OK) {
+ gchar *base_dn, *filter, *attributes;
+ GdaLdapSearchScope scope;
+ GError *lerror = NULL;
+ const gchar *tname;
+ gboolean replace;
+ filter_editor_get_settings (FILTER_EDITOR (epage->priv->search_entry),
+ &base_dn, &filter, &attributes, &scope);
+ tname = vtable_dialog_get_table_name (VTABLE_DIALOG (dlg));
+ replace = vtable_dialog_get_replace_if_exists (VTABLE_DIALOG (dlg));
+ if (replace)
+ browser_connection_undeclare_table (epage->priv->bcnc, tname, NULL);
+ if (! browser_connection_declare_table (epage->priv->bcnc, tname, base_dn, filter,
+ attributes, scope, &lerror)) {
+ browser_show_error (parent,
+ _("Could not define virtual table for this LDAP search: %s"),
+ lerror && lerror->message ? lerror->message : _("No detail"));
+ g_clear_error (&lerror);
+ }
+ else
+ browser_show_message (parent,
+ _("Virtual table '%s' for this LDAP search has been defined"),
+ tname);
+ }
+ gtk_widget_destroy (dlg);
+}
+/*
+static void
+action_class_back_cb (G_GNUC_UNUSED GtkAction *action, LdapSearchPage *epage)
+{
+ epage->priv->add_hist_item = FALSE;
+ if (epage->priv->current_hitem > 0) {
+ HistoryItem *hitem = NULL;
+ gboolean use_dn = TRUE;
+ hitem = g_array_index (epage->priv->history_items, HistoryItem*,
+ epage->priv->current_hitem - 1);
+ if (hitem->reference) {
+ if (gtk_tree_row_reference_valid (hitem->reference)) {
+ GtkTreeSelection *sel;
+ GtkTreePath *path;
+ path = gtk_tree_row_reference_get_path (hitem->reference);
+ sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (epage->priv->search_view));
+ gtk_tree_selection_select_path (sel, path);
+ gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (epage->priv->search_view),
+ path, NULL, TRUE, 0.5, 0.5);
+ gtk_tree_path_free (path);
+ use_dn = FALSE;
+ }
+ else {
+ gtk_tree_row_reference_free (hitem->reference);
+ hitem->reference = NULL;
+ }
+ }
+ if (use_dn)
+ search_view_set_current_class (SEARCH_VIEW (epage->priv->search_view),
+ hitem->classname);
+ }
+ epage->priv->add_hist_item = TRUE;
+}
+
+static void
+action_class_forward_cb (G_GNUC_UNUSED GtkAction *action, LdapSearchPage *epage)
+{
+ epage->priv->add_hist_item = FALSE;
+ if ((epage->priv->current_hitem >=0) &&
+ ((guint) epage->priv->current_hitem < epage->priv->history_items->len)) {
+ HistoryItem *hitem = NULL;
+ gboolean use_dn = TRUE;
+ hitem = g_array_index (epage->priv->history_items, HistoryItem*,
+ epage->priv->current_hitem + 1);
+ if (hitem->reference) {
+ if (gtk_tree_row_reference_valid (hitem->reference)) {
+ GtkTreeSelection *sel;
+ GtkTreePath *path;
+ path = gtk_tree_row_reference_get_path (hitem->reference);
+ sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (epage->priv->search_view));
+ gtk_tree_selection_select_path (sel, path);
+ gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (epage->priv->search_view),
+ path, NULL, TRUE, 0.5, 0.5);
+ gtk_tree_path_free (path);
+ use_dn = FALSE;
+ }
+ else {
+ gtk_tree_row_reference_free (hitem->reference);
+ hitem->reference = NULL;
+ }
+ }
+ if (use_dn)
+ search_view_set_current_class (SEARCH_VIEW (epage->priv->search_view),
+ hitem->classname);
+ }
+ epage->priv->add_hist_item = TRUE;
+}
+
+*/
+static GtkActionEntry ui_actions[] = {
+ { "LDAP", NULL, N_("_LDAP"), NULL, N_("LDAP"), NULL },
+ { "DefineAsTable", BROWSER_STOCK_TABLE_ADD, N_("Define as table"), NULL, N_("Define search as a virtual table"),
+ G_CALLBACK (action_define_as_table_cb)},
+ /*
+ { "DnBack", GTK_STOCK_GO_BACK, N_("Previous class"), NULL, N_("Move back to previous LDAP class"),
+ G_CALLBACK (action_class_back_cb)},
+ { "DnForward", GTK_STOCK_GO_FORWARD, N_("Next class"), NULL, N_("Move to next LDAP class"),
+ G_CALLBACK (action_class_forward_cb)},
+ */
+};
+static const gchar *ui_actions_page =
+ "<ui>"
+ " <menubar name='MenuBar'>"
+ " <placeholder name='MenuExtension'>"
+ " <menu name='LDAP' action='LDAP'>"
+ " <menuitem name='DefineAsTable' action= 'DefineAsTable'/>"
+ /*" <separator/>"
+ " <menuitem name='DnBack' action= 'DnBack'/>"
+ " <menuitem name='DnForward' action= 'DnForward'/>"*/
+ " </menu>"
+ " </placeholder>"
+ " </menubar>"
+ " <toolbar name='ToolBar'>"
+ " <separator/>"
+ " <toolitem action='DefineAsTable'/>"
+ /*" <toolitem action='DnBack'/>"
+ " <toolitem action='DnForward'/>"*/
+ " </toolbar>"
+ "</ui>";
+
+static GtkActionGroup *
+ldap_search_page_page_get_actions_group (BrowserPage *page)
+{
+ LdapSearchPage *epage;
+
+ epage = LDAP_SEARCH_PAGE (page);
+ if (! epage->priv->agroup) {
+ GtkActionGroup *agroup;
+ agroup = gtk_action_group_new ("LdapLdapSearchPageActions");
+ gtk_action_group_set_translation_domain (agroup, GETTEXT_PACKAGE);
+ gtk_action_group_add_actions (agroup, ui_actions, G_N_ELEMENTS (ui_actions), page);
+ epage->priv->agroup = agroup;
+
+ GtkAction *action;
+ gchar *base_dn, *filter, *attributes;
+ gboolean act = FALSE;
+ action = gtk_action_group_get_action (agroup, "DefineAsTable");
+ filter_editor_get_settings (FILTER_EDITOR (epage->priv->search_entry),
+ &base_dn, &filter, &attributes, NULL);
+ if ((base_dn && *base_dn) || (filter && *filter) || (attributes && *attributes))
+ act = TRUE;
+ g_free (base_dn);
+ g_free (filter);
+ g_free (attributes);
+ gtk_action_set_sensitive (action, act);
+
+ update_history_actions (epage);
+ }
+
+ return g_object_ref (epage->priv->agroup);
+}
+
+static const gchar *
+ldap_search_page_page_get_actions_ui (G_GNUC_UNUSED BrowserPage *page)
+{
+ return ui_actions_page;
+}
+
+static GtkWidget *
+ldap_search_page_page_get_tab_label (BrowserPage *page, GtkWidget **out_close_button)
+{
+ LdapSearchPage *epage;
+ const gchar *tab_name;
+ GdkPixbuf *search_pixbuf;
+
+ epage = LDAP_SEARCH_PAGE (page);
+ search_pixbuf = gtk_widget_render_icon (GTK_WIDGET (page), GTK_STOCK_FIND, GTK_ICON_SIZE_MENU, NULL);
+ tab_name = _("LDAP search");
+ return browser_make_tab_label_with_pixbuf (tab_name,
+ search_pixbuf,
+ out_close_button ? TRUE : FALSE, out_close_button);
+}
diff --git a/tools/browser/ldap-browser/ldap-search-page.h b/tools/browser/ldap-browser/ldap-search-page.h
new file mode 100644
index 0000000..4d9d513
--- /dev/null
+++ b/tools/browser/ldap-browser/ldap-search-page.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2011 The GNOME Foundation
+ *
+ * AUTHORS:
+ * Vivien Malerba <malerba gnome-db org>
+ *
+ * This Program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __LDAP_SEARCH_PAGE_H__
+#define __LDAP_SEARCH_PAGE_H__
+
+#include <gtk/gtk.h>
+#include "../browser-connection.h"
+
+G_BEGIN_DECLS
+
+#define LDAP_SEARCH_PAGE_TYPE (ldap_search_page_get_type())
+#define LDAP_SEARCH_PAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, LDAP_SEARCH_PAGE_TYPE, LdapSearchPage))
+#define LDAP_LDAP_SEARCH_PAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, LDAP_SEARCH_PAGE_TYPE, LdapSearchPageClass))
+#define IS_LDAP_LDAP_SEARCH_PAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, LDAP_SEARCH_PAGE_TYPE))
+#define IS_LDAP_LDAP_LDAP_SEARCH_PAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), LDAP_SEARCH_PAGE_TYPE))
+
+typedef struct _LdapSearchPage LdapSearchPage;
+typedef struct _LdapSearchPageClass LdapSearchPageClass;
+typedef struct _LdapSearchPagePrivate LdapSearchPagePrivate;
+
+struct _LdapSearchPage {
+ GtkVBox parent;
+ LdapSearchPagePrivate *priv;
+};
+
+struct _LdapSearchPageClass {
+ GtkVBoxClass parent_class;
+};
+
+GType ldap_search_page_get_type (void) G_GNUC_CONST;
+
+GtkWidget *ldap_search_page_new (BrowserConnection *bcnc, const gchar *base_dn);
+
+G_END_DECLS
+
+#endif
diff --git a/tools/browser/ldap-browser/marshal.list b/tools/browser/ldap-browser/marshal.list
new file mode 100644
index 0000000..fc405d8
--- /dev/null
+++ b/tools/browser/ldap-browser/marshal.list
@@ -0,0 +1,27 @@
+# see glib-genmarshal(1) for a detailed description of the file format,
+# possible parameter types are:
+# VOID indicates no return type, or no extra
+# parameters. if VOID is used as the parameter
+# list, no additional parameters may be present.
+# BOOLEAN for boolean types (gboolean)
+# CHAR for signed char types (gchar)
+# UCHAR for unsigned char types (guchar)
+# INT for signed integer types (gint)
+# UINT for unsigned integer types (guint)
+# LONG for signed long integer types (glong)
+# ULONG for unsigned long integer types (gulong)
+# ENUM for enumeration types (gint)
+# FLAGS for flag enumeration types (guint)
+# FLOAT for single-precision float types (gfloat)
+# DOUBLE for double-precision float types (gdouble)
+# STRING for string types (gchar*)
+# PARAM for GParamSpec or derived types (GParamSpec*)
+# BOXED for boxed (anonymous but reference counted) types (GBoxed*)
+# POINTER for anonymous pointer types (gpointer)
+# OBJECT for GObject or derived types (GObject*)
+# NONE deprecated alias for VOID
+# BOOL deprecated alias for BOOLEAN
+
+VOID:ENUM,STRING
+VOID:INT,ENUM,STRING
+VOID:STRING
diff --git a/tools/browser/ldap-browser/mgr-ldap-classes.c b/tools/browser/ldap-browser/mgr-ldap-classes.c
new file mode 100644
index 0000000..a554db7
--- /dev/null
+++ b/tools/browser/ldap-browser/mgr-ldap-classes.c
@@ -0,0 +1,347 @@
+/*
+ * Copyright (C) 2011 The GNOME Foundation.
+ *
+ * AUTHORS:
+ * Vivien Malerba <malerba gnome-db org>
+ *
+ * This Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <glib/gi18n-lib.h>
+#include <libgda/libgda.h>
+#include "mgr-ldap-classes.h"
+#include "gda-tree-node.h"
+#include <sqlite/virtual/gda-ldap-connection.h>
+
+struct _MgrLdapClassesPriv {
+ BrowserConnection *bcnc;
+ gchar *class;
+ gboolean flat;
+};
+
+static void mgr_ldap_classes_class_init (MgrLdapClassesClass *klass);
+static void mgr_ldap_classes_init (MgrLdapClasses *tmgr1, MgrLdapClassesClass *klass);
+static void mgr_ldap_classes_dispose (GObject *object);
+
+/* virtual methods */
+static GSList *mgr_ldap_classes_update_children (GdaTreeManager *manager, GdaTreeNode *node,
+ const GSList *children_nodes,
+ gboolean *out_error, GError **error);
+
+static GObjectClass *parent_class = NULL;
+
+/*
+ * MgrLdapClasses class implementation
+ * @klass:
+ */
+static void
+mgr_ldap_classes_class_init (MgrLdapClassesClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ /* virtual methods */
+ ((GdaTreeManagerClass*) klass)->update_children = mgr_ldap_classes_update_children;
+
+ object_class->dispose = mgr_ldap_classes_dispose;
+}
+
+static void
+mgr_ldap_classes_init (MgrLdapClasses *mgr, G_GNUC_UNUSED MgrLdapClassesClass *klass)
+{
+ g_return_if_fail (MGR_IS_LDAP_CLASSES (mgr));
+ mgr->priv = g_new0 (MgrLdapClassesPriv, 1);
+}
+
+static void
+mgr_ldap_classes_dispose (GObject *object)
+{
+ MgrLdapClasses *mgr = (MgrLdapClasses *) object;
+
+ g_return_if_fail (MGR_IS_LDAP_CLASSES (mgr));
+
+ if (mgr->priv) {
+ if (mgr->priv->bcnc)
+ g_object_unref (mgr->priv->bcnc);
+ g_free (mgr->priv->class);
+ g_free (mgr->priv);
+ mgr->priv = NULL;
+ }
+
+ /* chain to parent class */
+ parent_class->dispose (object);
+}
+
+/**
+ * mgr_ldap_classes_get_type:
+ *
+ * Returns: the GType
+ */
+GType
+mgr_ldap_classes_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0)) {
+ static GStaticMutex registering = G_STATIC_MUTEX_INIT;
+ static const GTypeInfo info = {
+ sizeof (MgrLdapClassesClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) mgr_ldap_classes_class_init,
+ NULL,
+ NULL,
+ sizeof (MgrLdapClasses),
+ 0,
+ (GInstanceInitFunc) mgr_ldap_classes_init,
+ 0
+ };
+
+ g_static_mutex_lock (®istering);
+ if (type == 0)
+ type = g_type_register_static (GDA_TYPE_TREE_MANAGER, "_MgrLdapClasses", &info, 0);
+ g_static_mutex_unlock (®istering);
+ }
+ return type;
+}
+
+/*
+ * mgr_ldap_classes_new:
+ * @cnc: a #GdaConnection object
+ * @flat: %TRUE if listing all the classes, if %TRUE, then @classname is ignored.
+ * @classname: (allow-none): an LDAP class or %NULL for the "top" class
+ *
+ * Creates a new #GdaTreeManager object which will list the children classes
+ *
+ * Returns: (transfer full): a new #GdaTreeManager object
+ */
+GdaTreeManager*
+mgr_ldap_classes_new (BrowserConnection *bcnc, gboolean flat, const gchar *classname)
+{
+ MgrLdapClasses *mgr;
+ g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), NULL);
+
+ mgr = (MgrLdapClasses*) g_object_new (MGR_TYPE_LDAP_CLASSES, NULL);
+
+ mgr->priv->bcnc = g_object_ref (bcnc);
+ mgr->priv->flat = flat;
+ if (!flat) {
+ if (classname)
+ mgr->priv->class = g_strdup (classname);
+ }
+
+ return (GdaTreeManager*) mgr;
+}
+
+static GSList *
+mgr_ldap_classes_update_children_flat (MgrLdapClasses *mgr, GdaTreeNode *node,
+ gboolean *out_error,
+ GError **error);
+static GSList *
+mgr_ldap_classes_update_children_nonflat (MgrLdapClasses *mgr, GdaTreeNode *node,
+ gboolean *out_error,
+ GError **error);
+
+static GSList *
+mgr_ldap_classes_update_children (GdaTreeManager *manager, GdaTreeNode *node,
+ G_GNUC_UNUSED const GSList *children_nodes, gboolean *out_error,
+ GError **error)
+{
+ MgrLdapClasses *mgr = MGR_LDAP_CLASSES (manager);
+
+ if (mgr->priv->flat)
+ return mgr_ldap_classes_update_children_flat (mgr, node, out_error, error);
+ else
+ return mgr_ldap_classes_update_children_nonflat (mgr, node, out_error, error);
+
+}
+
+static gint
+class_sort_func (GdaLdapClass *lcl1, GdaLdapClass *lcl2)
+{
+ if (lcl1->kind == lcl2->kind)
+ return g_ascii_strcasecmp (lcl1->names[0], lcl2->names[0]);
+ else
+ return lcl1->kind - lcl2->kind;
+}
+
+static GSList *
+mgr_ldap_classes_update_children_nonflat (MgrLdapClasses *mgr, GdaTreeNode *node,
+ gboolean *out_error,
+ GError **error)
+{
+ gchar *real_class = NULL;
+ if (!mgr->priv->bcnc) {
+ g_set_error (error, GDA_TREE_MANAGER_ERROR, GDA_TREE_MANAGER_UNKNOWN_ERROR,
+ _("No LDAP connection specified"));
+ if (out_error)
+ *out_error = TRUE;
+ goto onerror;
+ }
+
+ const GValue *cvalue;
+ cvalue = gda_tree_node_fetch_attribute (node, "kind");
+ if (cvalue && (G_VALUE_TYPE (cvalue) == G_TYPE_BOOLEAN) && g_value_get_boolean (cvalue))
+ return NULL;
+
+ if (node) {
+ /* looking for a dn in @node's attributes */
+ cvalue = gda_tree_node_fetch_attribute (node, "class");
+ if (cvalue && (G_VALUE_TYPE (cvalue) == G_TYPE_STRING))
+ real_class = g_value_dup_string (cvalue);
+ }
+ if (!real_class)
+ real_class = g_strdup (mgr->priv->class);
+
+ GSList *classes_list;
+ gboolean list_to_free = FALSE;
+ if (real_class) {
+ GdaLdapClass *lcl;
+ lcl = browser_connection_get_class_info (mgr->priv->bcnc, real_class);
+ if (!lcl)
+ goto onerror;
+ classes_list = (GSList*) lcl->children;
+ }
+ else {
+ /* sort by kind */
+ classes_list = g_slist_copy ((GSList*) browser_connection_get_top_classes (mgr->priv->bcnc));
+ classes_list = g_slist_sort (classes_list, (GCompareFunc) class_sort_func);
+ list_to_free = TRUE;
+ }
+
+ GSList *list = NULL;
+ const GSList *child;
+ for (child = classes_list; child; child = child->next) {
+ GdaLdapClass *sub;
+ GdaTreeNode* snode;
+ GValue *value;
+ GdkPixbuf *pixbuf;
+
+ sub = (GdaLdapClass*) child->data;
+
+ snode = gda_tree_manager_create_node ((GdaTreeManager*) mgr, node, sub->names[0]);
+
+ /* class name */
+ g_value_set_string ((value = gda_value_new (G_TYPE_STRING)), sub->names[0]);
+ gda_tree_node_set_node_attribute (snode, "class", value, NULL);
+ gda_value_free (value);
+
+ /* icon */
+ pixbuf = browser_get_pixbuf_for_ldap_class (sub->kind);
+ value = gda_value_new (G_TYPE_OBJECT);
+ g_value_set_object (value, pixbuf);
+ gda_tree_node_set_node_attribute (snode, "icon", value, NULL);
+ gda_value_free (value);
+
+ list = g_slist_prepend (list, snode);
+ }
+ if (list_to_free)
+ g_slist_free (classes_list);
+ return g_slist_reverse (list);
+
+ onerror:
+ g_free (real_class);
+ if (out_error)
+ *out_error = TRUE;
+ return NULL;
+}
+
+static void
+classes_foreach_func (GdaLdapClass *lcl, GSList **list)
+{
+ if (!g_slist_find (*list, lcl))
+ *list = g_slist_insert_sorted (*list, lcl, (GCompareFunc) class_sort_func);
+ g_slist_foreach (lcl->children, (GFunc) classes_foreach_func, list);
+}
+
+static GSList *
+mgr_ldap_classes_update_children_flat (MgrLdapClasses *mgr, GdaTreeNode *node,
+ gboolean *out_error,
+ GError **error)
+{
+ if (!mgr->priv->bcnc) {
+ g_set_error (error, GDA_TREE_MANAGER_ERROR, GDA_TREE_MANAGER_UNKNOWN_ERROR,
+ _("No LDAP connection specified"));
+ if (out_error)
+ *out_error = TRUE;
+ goto onerror;
+ }
+
+ const GSList *top_classes_list;
+ GSList *list = NULL, *classes_list = NULL;
+ top_classes_list = browser_connection_get_top_classes (mgr->priv->bcnc);
+ for (list = (GSList*) top_classes_list; list; list = list->next) {
+ GdaLdapClass *lcl;
+ lcl = (GdaLdapClass*) list->data;
+ classes_list = g_slist_insert_sorted (classes_list, lcl, (GCompareFunc) class_sort_func);
+ g_slist_foreach (lcl->children, (GFunc) classes_foreach_func, &classes_list);
+ }
+
+ GSList *child;
+ GdaLdapClassKind kind = 0;
+ for (list = NULL, child = classes_list; child; child = child->next) {
+ GdaLdapClass *sub;
+ GdaTreeNode* snode;
+ GValue *value;
+ GdkPixbuf *pixbuf;
+
+ sub = (GdaLdapClass*) child->data;
+
+ if (kind != sub->kind) {
+ /* add extra node as separator */
+ const gchar *tmp;
+ tmp = browser_get_kind_for_ldap_class (sub->kind);
+ snode = gda_tree_manager_create_node ((GdaTreeManager*) mgr, node, tmp);
+ list = g_slist_prepend (list, snode);
+ kind = sub->kind;
+
+ /* marker */
+ g_value_set_boolean ((value = gda_value_new (G_TYPE_BOOLEAN)), TRUE);
+ gda_tree_node_set_node_attribute (snode, "kind", value, NULL);
+ gda_value_free (value);
+
+ /* icon */
+ pixbuf = browser_get_pixbuf_for_ldap_class (sub->kind);
+ value = gda_value_new (G_TYPE_OBJECT);
+ g_value_set_object (value, pixbuf);
+ gda_tree_node_set_node_attribute (snode, "icon", value, NULL);
+ gda_value_free (value);
+ }
+ snode = gda_tree_manager_create_node ((GdaTreeManager*) mgr, node, sub->names[0]);
+
+ /* class name */
+ g_value_set_string ((value = gda_value_new (G_TYPE_STRING)), sub->names[0]);
+ gda_tree_node_set_node_attribute (snode, "class", value, NULL);
+ gda_value_free (value);
+
+ /* icon */
+ pixbuf = browser_get_pixbuf_for_ldap_class (sub->kind);
+ value = gda_value_new (G_TYPE_OBJECT);
+ g_value_set_object (value, pixbuf);
+ gda_tree_node_set_node_attribute (snode, "icon", value, NULL);
+ gda_value_free (value);
+
+ list = g_slist_prepend (list, snode);
+ }
+ g_slist_free (classes_list);
+ return g_slist_reverse (list);
+
+ onerror:
+ if (out_error)
+ *out_error = TRUE;
+ return NULL;
+}
diff --git a/tools/browser/ldap-browser/mgr-ldap-classes.h b/tools/browser/ldap-browser/mgr-ldap-classes.h
new file mode 100644
index 0000000..d2329d9
--- /dev/null
+++ b/tools/browser/ldap-browser/mgr-ldap-classes.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2011 The GNOME Foundation.
+ *
+ * AUTHORS:
+ * Vivien Malerba <malerba gnome-db org>
+ *
+ * This Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __MGR_LDAP_CLASSES_H__
+#define __MGR_LDAP_CLASSES_H__
+
+#include "../browser-connection.h"
+#include "gda-tree-manager.h"
+
+G_BEGIN_DECLS
+
+#define MGR_TYPE_LDAP_CLASSES (mgr_ldap_classes_get_type())
+#define MGR_LDAP_CLASSES(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, MGR_TYPE_LDAP_CLASSES, MgrLdapClasses))
+#define MGR_LDAP_CLASSES_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, MGR_TYPE_LDAP_CLASSES, MgrLdapClassesClass))
+#define MGR_IS_LDAP_CLASSES(obj) (G_TYPE_CHECK_INSTANCE_TYPE(obj, MGR_TYPE_LDAP_CLASSES))
+#define MGR_IS_LDAP_CLASSES_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), MGR_TYPE_LDAP_CLASSES))
+#define MGR_LDAP_CLASSES_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), MGR_TYPE_LDAP_CLASSES, MgrLdapClassesClass))
+
+typedef struct _MgrLdapClasses MgrLdapClasses;
+typedef struct _MgrLdapClassesPriv MgrLdapClassesPriv;
+typedef struct _MgrLdapClassesClass MgrLdapClassesClass;
+
+struct _MgrLdapClasses {
+ GdaTreeManager object;
+ MgrLdapClassesPriv *priv;
+};
+
+struct _MgrLdapClassesClass {
+ GdaTreeManagerClass object_class;
+};
+
+GType mgr_ldap_classes_get_type (void) G_GNUC_CONST;
+GdaTreeManager* mgr_ldap_classes_new (BrowserConnection *bcnc, gboolean flat, const gchar *classname);
+
+G_END_DECLS
+
+#endif
diff --git a/tools/browser/ldap-browser/mgr-ldap-entries.c b/tools/browser/ldap-browser/mgr-ldap-entries.c
new file mode 100644
index 0000000..44b3ed6
--- /dev/null
+++ b/tools/browser/ldap-browser/mgr-ldap-entries.c
@@ -0,0 +1,309 @@
+/*
+ * Copyright (C) 2011 The GNOME Foundation.
+ *
+ * AUTHORS:
+ * Vivien Malerba <malerba gnome-db org>
+ *
+ * This Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <glib/gi18n-lib.h>
+#include <libgda/libgda.h>
+#include "mgr-ldap-entries.h"
+#include <libgda/gda-tree-node.h>
+
+struct _MgrLdapEntriesPriv {
+ BrowserConnection *bcnc;
+ gchar *dn;
+};
+
+static void mgr_ldap_entries_class_init (MgrLdapEntriesClass *klass);
+static void mgr_ldap_entries_init (MgrLdapEntries *tmgr1, MgrLdapEntriesClass *klass);
+static void mgr_ldap_entries_dispose (GObject *object);
+
+/* virtual methods */
+static GSList *mgr_ldap_entries_update_children (GdaTreeManager *manager, GdaTreeNode *node,
+ const GSList *children_nodes,
+ gboolean *out_error, GError **error);
+
+static GObjectClass *parent_class = NULL;
+
+/*
+ * MgrLdapEntries class implementation
+ * @klass:
+ */
+static void
+mgr_ldap_entries_class_init (MgrLdapEntriesClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ /* virtual methods */
+ ((GdaTreeManagerClass*) klass)->update_children = mgr_ldap_entries_update_children;
+
+ /* Properties */
+ object_class->dispose = mgr_ldap_entries_dispose;
+}
+
+static void
+mgr_ldap_entries_init (MgrLdapEntries *mgr, G_GNUC_UNUSED MgrLdapEntriesClass *klass)
+{
+ g_return_if_fail (MGR_IS_LDAP_ENTRIES (mgr));
+ mgr->priv = g_new0 (MgrLdapEntriesPriv, 1);
+}
+
+static void
+mgr_ldap_entries_dispose (GObject *object)
+{
+ MgrLdapEntries *mgr = (MgrLdapEntries *) object;
+
+ g_return_if_fail (MGR_IS_LDAP_ENTRIES (mgr));
+
+ if (mgr->priv) {
+ if (mgr->priv->bcnc)
+ g_object_unref (mgr->priv->bcnc);
+ g_free (mgr->priv->dn);
+ g_free (mgr->priv);
+ mgr->priv = NULL;
+ }
+
+ /* chain to parent class */
+ parent_class->dispose (object);
+}
+
+/**
+ * browser_tree_mgr_select_get_type:
+ *
+ * Returns: the GType
+ */
+GType
+mgr_ldap_entries_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0)) {
+ static GStaticMutex registering = G_STATIC_MUTEX_INIT;
+ static const GTypeInfo info = {
+ sizeof (MgrLdapEntriesClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) mgr_ldap_entries_class_init,
+ NULL,
+ NULL,
+ sizeof (MgrLdapEntries),
+ 0,
+ (GInstanceInitFunc) mgr_ldap_entries_init,
+ 0
+ };
+
+ g_static_mutex_lock (®istering);
+ if (type == 0)
+ type = g_type_register_static (GDA_TYPE_TREE_MANAGER, "MgrLdapEntries", &info, 0);
+ g_static_mutex_unlock (®istering);
+ }
+ return type;
+}
+
+/**
+ * mgr_ldap_entries_new:
+ * @cnc: a #BrowserConnection object
+ * @dn: (allow-none): a schema name or %NULL
+ *
+ * Creates a new #BrowserTreeManager object which will list the children of the LDAP entry which Distinguished name
+ * is @dn. If @dn is %NULL, then the tree manager will look in the tree itself for an attribute named "dn" and
+ * use it.
+ *
+ * Returns: (transfer full): a new #BrowserTreeManager object
+ */
+GdaTreeManager*
+mgr_ldap_entries_new (BrowserConnection *bcnc, const gchar *dn)
+{
+ MgrLdapEntries *mgr;
+ g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), NULL);
+
+ mgr = (MgrLdapEntries*) g_object_new (MGR_TYPE_LDAP_ENTRIES, NULL);
+
+ mgr->priv->bcnc = g_object_ref (bcnc);
+ if (dn)
+ mgr->priv->dn = g_strdup (dn);
+
+ return (GdaTreeManager*) mgr;
+}
+
+typedef struct {
+ GMainLoop *loop;
+ GdaLdapEntry **entries;
+ GError *error;
+} AsyncExecData;
+
+static void
+update_children_cb (G_GNUC_UNUSED BrowserConnection *bcnc,
+ gpointer out_result, AsyncExecData *data, GError *error)
+{
+ data->entries = (GdaLdapEntry **) out_result;
+ if (! data->entries && error)
+ data->error = g_error_copy (error);
+ g_main_loop_quit (data->loop);
+}
+
+static gint
+lentry_array_sort_func (gconstpointer a, gconstpointer b)
+{
+ GdaLdapEntry *e1, *e2;
+ e1 = *((GdaLdapEntry**) a);
+ e2 = *((GdaLdapEntry**) b);
+ GdaLdapAttribute *cna1, *cna2;
+ const gchar *str1 = NULL, *str2 = NULL;
+
+ cna1 = g_hash_table_lookup (e1->attributes_hash, "cn");
+ cna2 = g_hash_table_lookup (e2->attributes_hash, "cn");
+ if (cna1 && (cna1->nb_values > 0) && (G_VALUE_TYPE (cna1->values[0]) == G_TYPE_STRING))
+ str1 = g_value_get_string (cna1->values[0]);
+ else
+ str1 = e1->dn ? e1->dn : "";
+ if (cna2 && (cna2->nb_values > 0) && (G_VALUE_TYPE (cna2->values[0]) == G_TYPE_STRING))
+ str2 = g_value_get_string (cna2->values[0]);
+ else
+ str2 = e2->dn ? e2->dn : "";
+
+ return strcmp (str2, str1);
+}
+
+static GSList *
+mgr_ldap_entries_update_children (GdaTreeManager *manager, GdaTreeNode *node,
+ G_GNUC_UNUSED const GSList *children_nodes, gboolean *out_error,
+ GError **error)
+{
+ MgrLdapEntries *mgr = MGR_LDAP_ENTRIES (manager);
+ gchar *real_dn = NULL;
+
+ g_return_val_if_fail (mgr->priv->bcnc, NULL);
+
+ if (mgr->priv->dn)
+ real_dn = g_strdup (mgr->priv->dn);
+ else if (node) {
+ /* looking for a dn in @node's attributes */
+ const GValue *cvalue;
+ cvalue = gda_tree_node_fetch_attribute (node, "dn");
+ if (cvalue && (G_VALUE_TYPE (cvalue) == G_TYPE_STRING))
+ real_dn = g_value_dup_string (cvalue);
+ }
+
+ AsyncExecData data;
+ guint id;
+ data.loop = NULL;
+ data.entries = NULL;
+ data.error = NULL;
+ gchar *attrs[] = {"objectClass", "cn", NULL};
+
+ id = browser_connection_ldap_get_entry_children (mgr->priv->bcnc, real_dn, attrs,
+ BROWSER_CONNECTION_JOB_CALLBACK (update_children_cb),
+ &data, error);
+ g_free (real_dn);
+ if (id == 0) {
+ if (out_error)
+ *out_error = TRUE;
+ return NULL;
+ }
+ data.loop = g_main_loop_new (NULL, FALSE);
+ g_main_loop_run (data.loop);
+ g_main_loop_unref (data.loop);
+
+ if (data.entries) {
+ gint i;
+ GSList *list = NULL;
+ GArray *sorted_array;
+ sorted_array = g_array_new (FALSE, FALSE, sizeof (GdaLdapEntry*));
+ for (i = 0; data.entries [i]; i++) {
+ GdaLdapEntry *lentry;
+ lentry = data.entries [i];
+ g_array_prepend_val (sorted_array, lentry);
+ }
+ g_free (data.entries);
+
+ g_array_sort (sorted_array, (GCompareFunc) lentry_array_sort_func);
+
+ for (i = 0; i < sorted_array->len; i++) {
+ GdaTreeNode* snode;
+ GValue *dnv;
+ GdaLdapEntry *lentry;
+
+ lentry = g_array_index (sorted_array, GdaLdapEntry*, i);
+ snode = gda_tree_manager_create_node (manager, node, lentry->dn);
+
+ /* full DN */
+ g_value_set_string ((dnv = gda_value_new (G_TYPE_STRING)), lentry->dn);
+ gda_tree_node_set_node_attribute (snode, "dn", dnv, NULL);
+ gda_value_free (dnv);
+
+ /* RDN */
+ gchar **array;
+ array = gda_ldap_dn_split (lentry->dn, FALSE);
+ if (array) {
+ g_value_set_string ((dnv = gda_value_new (G_TYPE_STRING)), array [0]);
+ gda_tree_node_set_node_attribute (snode, "rdn", dnv, NULL);
+ gda_value_free (dnv);
+ g_strfreev (array);
+ }
+
+ /* CN */
+ GdaLdapAttribute *attr;
+ attr = g_hash_table_lookup (lentry->attributes_hash, "cn");
+ if (attr && (attr->nb_values >= 1)) {
+ const GValue *cvalue;
+ cvalue = attr->values [0];
+ if (cvalue && (G_VALUE_TYPE (cvalue) == G_TYPE_STRING))
+ gda_tree_node_set_node_attribute (snode, "cn", cvalue, NULL);
+ }
+
+ /* icon */
+ GdkPixbuf *pixbuf;
+ attr = g_hash_table_lookup (lentry->attributes_hash, "objectClass");
+ pixbuf = browser_connection_ldap_icon_for_class (attr);
+
+ dnv = gda_value_new (G_TYPE_OBJECT);
+ g_value_set_object (dnv, pixbuf);
+ gda_tree_node_set_node_attribute (snode, "icon", dnv, NULL);
+ gda_value_free (dnv);
+
+ if (gda_tree_manager_get_managers (manager)) {
+ g_value_set_boolean ((dnv = gda_value_new (G_TYPE_BOOLEAN)), TRUE);
+ gda_tree_node_set_node_attribute (snode,
+ GDA_ATTRIBUTE_TREE_NODE_UNKNOWN_CHILDREN,
+ dnv, NULL);
+ gda_value_free (dnv);
+ }
+
+ list = g_slist_prepend (list, snode);
+ gda_ldap_entry_free (lentry);
+ }
+ g_array_free (sorted_array, TRUE);
+
+ if (node)
+ gda_tree_node_set_node_attribute (node,
+ GDA_ATTRIBUTE_TREE_NODE_UNKNOWN_CHILDREN,
+ NULL, NULL);
+
+ return list;
+ }
+ else {
+ g_propagate_error (error, data.error);
+ if (out_error)
+ *out_error = TRUE;
+ return NULL;
+ }
+}
diff --git a/tools/browser/ldap-browser/mgr-ldap-entries.h b/tools/browser/ldap-browser/mgr-ldap-entries.h
new file mode 100644
index 0000000..09dbc71
--- /dev/null
+++ b/tools/browser/ldap-browser/mgr-ldap-entries.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2011 The GNOME Foundation.
+ *
+ * AUTHORS:
+ * Vivien Malerba <malerba gnome-db org>
+ *
+ * This Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __MGR_LDAP_ENTRIES_H__
+#define __MGR_LDAP_ENTRIES_H__
+
+#include "../browser-connection.h"
+#include <libgda/gda-tree-manager.h>
+
+G_BEGIN_DECLS
+
+#define MGR_TYPE_LDAP_ENTRIES (mgr_ldap_entries_get_type())
+#define MGR_LDAP_ENTRIES(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, MGR_TYPE_LDAP_ENTRIES, MgrLdapEntries))
+#define MGR_LDAP_ENTRIES_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, MGR_TYPE_LDAP_ENTRIES, MgrLdapEntriesClass))
+#define MGR_IS_LDAP_ENTRIES(obj) (G_TYPE_CHECK_INSTANCE_TYPE(obj, MGR_TYPE_LDAP_ENTRIES))
+#define MGR_IS_LDAP_ENTRIES_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), MGR_TYPE_LDAP_ENTRIES))
+#define MGR_LDAP_ENTRIES_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), MGR_TYPE_LDAP_ENTRIES, MgrLdapEntriesClass))
+
+typedef struct _MgrLdapEntries MgrLdapEntries;
+typedef struct _MgrLdapEntriesPriv MgrLdapEntriesPriv;
+typedef struct _MgrLdapEntriesClass MgrLdapEntriesClass;
+
+struct _MgrLdapEntries {
+ GdaTreeManager object;
+ MgrLdapEntriesPriv *priv;
+};
+
+struct _MgrLdapEntriesClass {
+ GdaTreeManagerClass object_class;
+};
+
+GType mgr_ldap_entries_get_type (void) G_GNUC_CONST;
+GdaTreeManager* mgr_ldap_entries_new (BrowserConnection *bcnc, const gchar *dn);
+
+G_END_DECLS
+
+#endif
diff --git a/tools/browser/ldap-browser/perspective-main.c b/tools/browser/ldap-browser/perspective-main.c
new file mode 100644
index 0000000..770bf5a
--- /dev/null
+++ b/tools/browser/ldap-browser/perspective-main.c
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2011 The GNOME Foundation
+ *
+ * AUTHORS:
+ * Vivien Malerba <malerba gnome-db org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <glib/gi18n-lib.h>
+#include <gmodule.h>
+#include "perspective-main.h"
+#include "ldap-browser-perspective.h"
+
+static BrowserPerspectiveFactory bfact;
+
+BrowserPerspectiveFactory *
+ldap_browser_perspective_get_factory (void)
+{
+ bfact.perspective_name = _("LDAP browser");
+ bfact.menu_shortcut = "<control>P";
+ bfact.perspective_create = ldap_browser_perspective_new;
+
+ return &bfact;
+}
diff --git a/tools/browser/ldap-browser/perspective-main.h b/tools/browser/ldap-browser/perspective-main.h
new file mode 100644
index 0000000..1a29d5b
--- /dev/null
+++ b/tools/browser/ldap-browser/perspective-main.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2011 The GNOME Foundation.
+ *
+ * AUTHORS:
+ * Vivien Malerba <malerba gnome-db org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "../decl.h"
+
+G_BEGIN_DECLS
+
+BrowserPerspectiveFactory *ldap_browser_perspective_get_factory (void);
+
+G_END_DECLS
+
diff --git a/tools/browser/ldap-browser/vtable-dialog.c b/tools/browser/ldap-browser/vtable-dialog.c
new file mode 100644
index 0000000..277b8f9
--- /dev/null
+++ b/tools/browser/ldap-browser/vtable-dialog.c
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2011 The GNOME Foundation
+ *
+ * AUTHORS:
+ * Vivien Malerba <malerba gnome-db org>
+ *
+ * This Program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <glib/gi18n-lib.h>
+#include <string.h>
+#include <gtk/gtk.h>
+#include "vtable-dialog.h"
+
+#define SPACING 3
+
+struct _VtableDialogPrivate {
+ BrowserConnection *bcnc;
+ GtkWidget *tname_entry;
+ GtkWidget *tname_replace;
+};
+
+static void vtable_dialog_class_init (VtableDialogClass *klass);
+static void vtable_dialog_init (VtableDialog *dlg, VtableDialogClass *klass);
+static void vtable_dialog_dispose (GObject *object);
+
+static GObjectClass *parent_class = NULL;
+
+
+/*
+ * VtableDialog class implementation
+ */
+
+static void
+vtable_dialog_class_init (VtableDialogClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ parent_class = g_type_class_peek_parent (klass);
+
+ object_class->dispose = vtable_dialog_dispose;
+}
+
+
+static void
+vtable_dialog_init (VtableDialog *dlg, G_GNUC_UNUSED VtableDialogClass *klass)
+{
+ dlg->priv = g_new0 (VtableDialogPrivate, 1);
+}
+
+static void
+vtable_dialog_dispose (GObject *object)
+{
+ VtableDialog *dlg = (VtableDialog *) object;
+
+ /* free memory */
+ if (dlg->priv) {
+ if (dlg->priv->bcnc)
+ g_object_unref (dlg->priv->bcnc);
+ g_free (dlg->priv);
+ dlg->priv = NULL;
+ }
+
+ parent_class->dispose (object);
+}
+
+GType
+vtable_dialog_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0)) {
+ static const GTypeInfo columns = {
+ sizeof (VtableDialogClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) vtable_dialog_class_init,
+ NULL,
+ NULL,
+ sizeof (VtableDialog),
+ 0,
+ (GInstanceInitFunc) vtable_dialog_init,
+ 0
+ };
+ type = g_type_register_static (GTK_TYPE_DIALOG, "VtableDialog", &columns, 0);
+ }
+ return type;
+}
+
+/**
+ * vtable_dialog_new:
+ *
+ * Returns: a new #GtkWidget
+ */
+GtkWidget *
+vtable_dialog_new (GtkWindow *parent, BrowserConnection *bcnc)
+{
+ VtableDialog *dlg;
+ g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), NULL);
+
+ dlg = VTABLE_DIALOG (g_object_new (VTABLE_DIALOG_TYPE, NULL));
+ dlg->priv->bcnc = g_object_ref (bcnc);
+
+ if (parent)
+ gtk_window_set_transient_for (GTK_WINDOW (dlg), parent);
+ gtk_window_set_modal (GTK_WINDOW (dlg), TRUE);
+ gtk_container_set_border_width (GTK_CONTAINER (dlg), SPACING * 2);
+ gtk_window_set_title (GTK_WINDOW (dlg), _("Define LDAP search as a virtual table"));
+
+ GtkWidget *dcontents;
+ GtkWidget *label, *entry, *table, *button;
+ gchar *str;
+ dcontents = gtk_dialog_get_content_area (GTK_DIALOG (dlg));
+ label = gtk_label_new (NULL);
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
+ str = g_markup_printf_escaped ("<b>%s:</b>\n<small>%s</small>",
+ _("Name of the virtual LDAP table to create"),
+ _("Everytime data is selected from the virtual table which will "
+ "be created, the LDAP search will be executed and data "
+ "returned as the contents of the table."));
+ gtk_label_set_markup (GTK_LABEL (label), str);
+ gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+ g_free (str);
+ gtk_box_pack_start (GTK_BOX (dcontents), label, FALSE, FALSE, SPACING);
+
+ table = gtk_table_new (2, 2, FALSE);
+ gtk_table_set_col_spacing (GTK_TABLE (table), 0, SPACING);
+ gtk_table_set_row_spacing (GTK_TABLE (table), 0, SPACING);
+ gtk_box_pack_start (GTK_BOX (dcontents), table, FALSE, FALSE, SPACING);
+
+ label = gtk_label_new (_("Table name:"));
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
+ gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1, GTK_FILL, 0, 0, 0);
+
+ entry = gtk_entry_new ();
+ gtk_table_attach_defaults (GTK_TABLE (table), entry, 1, 2, 0, 1);
+ dlg->priv->tname_entry = entry;
+
+ label = gtk_label_new (_("Replace if exists:"));
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
+ gtk_table_attach (GTK_TABLE (table), label, 0, 1, 1, 2, GTK_FILL, 0, 0, 0);
+
+ button = gtk_check_button_new ();
+ gtk_table_attach_defaults (GTK_TABLE (table), button, 1, 2, 1, 2);
+ dlg->priv->tname_replace = button;
+
+ gtk_widget_show_all (dcontents);
+ gtk_dialog_add_buttons (GTK_DIALOG (dlg),
+ GTK_STOCK_OK, GTK_RESPONSE_OK,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL);
+
+ return (GtkWidget*) dlg;
+}
+
+/**
+ * vtable_dialog_get_table_name:
+ *
+ */
+const gchar *
+vtable_dialog_get_table_name (VtableDialog *dlg)
+{
+ g_return_val_if_fail (IS_VTABLE_DIALOG (dlg), NULL);
+ return gtk_entry_get_text (GTK_ENTRY (dlg->priv->tname_entry));
+}
+
+/**
+ * vtable_dialog_get_replace_if_exists:
+ */
+gboolean
+vtable_dialog_get_replace_if_exists (VtableDialog *dlg)
+{
+ g_return_val_if_fail (IS_VTABLE_DIALOG (dlg), FALSE);
+ return gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (dlg->priv->tname_replace));
+}
diff --git a/tools/browser/ldap-browser/vtable-dialog.h b/tools/browser/ldap-browser/vtable-dialog.h
new file mode 100644
index 0000000..db265f9
--- /dev/null
+++ b/tools/browser/ldap-browser/vtable-dialog.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2011 The GNOME Foundation
+ *
+ * AUTHORS:
+ * Vivien Malerba <malerba gnome-db org>
+ *
+ * This Program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __VTABLE_DIALOG_H__
+#define __VTABLE_DIALOG_H__
+
+#include "../browser-connection.h"
+
+G_BEGIN_DECLS
+
+#define VTABLE_DIALOG_TYPE (vtable_dialog_get_type())
+#define VTABLE_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, VTABLE_DIALOG_TYPE, VtableDialog))
+#define VTABLE_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, VTABLE_DIALOG_TYPE, VtableDialogClass))
+#define IS_VTABLE_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, VTABLE_DIALOG_TYPE))
+#define IS_VTABLE_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), VTABLE_DIALOG_TYPE))
+
+typedef struct _VtableDialog VtableDialog;
+typedef struct _VtableDialogClass VtableDialogClass;
+typedef struct _VtableDialogPrivate VtableDialogPrivate;
+
+struct _VtableDialog {
+ GtkDialog parent;
+ VtableDialogPrivate *priv;
+};
+
+struct _VtableDialogClass {
+ GtkDialogClass parent_class;
+};
+
+GType vtable_dialog_get_type (void) G_GNUC_CONST;
+
+GtkWidget *vtable_dialog_new (GtkWindow *parent, BrowserConnection *bcnc);
+const gchar *vtable_dialog_get_table_name (VtableDialog *dlg);
+gboolean vtable_dialog_get_replace_if_exists (VtableDialog *dlg);
+
+G_END_DECLS
+
+#endif
diff --git a/tools/browser/main.c b/tools/browser/main.c
index c82706e..1046e63 100644
--- a/tools/browser/main.c
+++ b/tools/browser/main.c
@@ -41,6 +41,9 @@
#include "schema-browser/perspective-main.h"
#include "query-exec/perspective-main.h"
#include "data-manager/perspective-main.h"
+#ifdef HAVE_LDAP
+#include "ldap-browser/perspective-main.h"
+#endif
/* #include "dummy-perspective/perspective-main.h" */
extern BrowserCoreInitFactories browser_core_init_factories;
@@ -52,6 +55,9 @@ main_browser_core_init_factories (void)
factories = g_slist_append (factories, schema_browser_perspective_get_factory ());
factories = g_slist_append (factories, query_exec_perspective_get_factory ());
factories = g_slist_append (factories, data_manager_perspective_get_factory ());
+#ifdef HAVE_LDAP
+ factories = g_slist_append (factories, ldap_browser_perspective_get_factory ());
+#endif
/* factories = g_slist_append (factories, dummy_perspective_get_factory ()); */
return factories;
}
@@ -246,7 +252,7 @@ main (int argc, char *argv[])
}
}
}
- gtk_widget_destroy (dialog);
+ gtk_widget_destroy ((GtkWidget*) dialog);
}
}
diff --git a/tools/browser/mgr-favorites.c b/tools/browser/mgr-favorites.c
index 73da388..1aad0e7 100644
--- a/tools/browser/mgr-favorites.c
+++ b/tools/browser/mgr-favorites.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009 The GNOME Foundation.
+ * Copyright (C) 2009 - 2011 The GNOME Foundation.
*
* AUTHORS:
* Vivien Malerba <malerba gnome-db org>
@@ -27,10 +27,26 @@
#include "mgr-favorites.h"
#include "support.h"
+/* asynchronous (in idle loop) icon resolution */
+typedef struct {
+ GdaTreeNode *node;
+ gchar *dn;
+} IconResolutionData;
+static void
+icon_resolution_data_free (IconResolutionData *data)
+{
+ g_object_unref (G_OBJECT (data->node));
+ g_free (data->dn);
+ g_free (data);
+}
+
struct _MgrFavoritesPriv {
BrowserConnection *bcnc;
BrowserFavoritesType fav_type;
gint order_key;
+
+ GSList *icons_resol_list; /* list of IconResolutionData pointers */
+ guint icons_resol_timer;
};
static void mgr_favorites_class_init (MgrFavoritesClass *klass);
@@ -97,6 +113,17 @@ mgr_favorites_dispose (GObject *object)
if (mgr->priv->bcnc)
g_object_unref (mgr->priv->bcnc);
+ if (mgr->priv->icons_resol_timer) {
+ g_source_remove (mgr->priv->icons_resol_timer);
+ mgr->priv->icons_resol_timer = 0;
+ }
+
+ if (mgr->priv->icons_resol_list) {
+ g_slist_foreach (mgr->priv->icons_resol_list, (GFunc) icon_resolution_data_free, NULL);
+ g_slist_free (mgr->priv->icons_resol_list);
+ mgr->priv->icons_resol_list = NULL;
+ }
+
g_free (mgr->priv);
mgr->priv = NULL;
}
@@ -328,6 +355,8 @@ hash_for_existing_nodes (const GSList *nodes)
return hash;
}
+static gboolean icons_resol_cb (MgrFavorites *mgr);
+
static GSList *
mgr_favorites_update_children (GdaTreeManager *manager, GdaTreeNode *node, const GSList *children_nodes,
gboolean *out_error, GError **error)
@@ -356,14 +385,17 @@ mgr_favorites_update_children (GdaTreeManager *manager, GdaTreeNode *node, const
BrowserFavoritesAttributes *fav = (BrowserFavoritesAttributes *) list->data;
GdaTreeNode* snode = NULL;
GValue *av;
+ gboolean newsnode = TRUE;
if (ehash)
snode = g_hash_table_lookup (ehash, &(fav->id));
- if (snode)
+ if (snode) {
/* use the same node */
g_object_ref (G_OBJECT (snode));
+ newsnode = FALSE;
+ }
if (fav->type == BROWSER_FAVORITES_TABLES) {
if (!snode) {
@@ -557,6 +589,159 @@ mgr_favorites_update_children (GdaTreeManager *manager, GdaTreeNode *node, const
av, NULL);
gda_value_free (av);
}
+#ifdef HAVE_LDAP
+ else if (fav->type == BROWSER_FAVORITES_LDAP_DN) {
+ if (!snode) {
+ /* favorite ID */
+ snode = gda_tree_manager_create_node (manager, node, NULL);
+
+ g_value_set_int ((av = gda_value_new (G_TYPE_INT)), fav->id);
+ gda_tree_node_set_node_attribute (snode,
+ MGR_FAVORITES_ID_ATT_NAME,
+ av, NULL);
+ gda_value_free (av);
+
+ /* icon */
+ GdkPixbuf *pixbuf;
+ pixbuf = browser_get_pixbuf_icon (BROWSER_ICON_LDAP_ENTRY);
+
+ av = gda_value_new (G_TYPE_OBJECT);
+ g_value_set_object (av, pixbuf);
+ gda_tree_node_set_node_attribute (snode, "icon", av, NULL);
+ gda_value_free (av);
+
+ g_value_set_uint ((av = gda_value_new (G_TYPE_UINT)), fav->type);
+ gda_tree_node_set_node_attribute (snode,
+ MGR_FAVORITES_TYPE_ATT_NAME,
+ av, NULL);
+ gda_value_free (av);
+
+ IconResolutionData *data;
+ data = g_new0 (IconResolutionData, 1);
+ data->node = g_object_ref (snode);
+ data->dn = g_strdup (fav->name);
+ mgr->priv->icons_resol_list = g_slist_prepend (mgr->priv->icons_resol_list, data);
+ if (mgr->priv->icons_resol_timer == 0)
+ mgr->priv->icons_resol_timer = g_idle_add ((GSourceFunc) icons_resol_cb, mgr);
+ }
+
+ gchar **dna;
+ GString *tmpstring;
+ dna = gda_ldap_dn_split (fav->name, FALSE);
+ tmpstring = g_string_new ("");
+ if (dna) {
+ if (dna[0]) {
+ if (dna[1])
+ g_string_append_printf (tmpstring,
+ "<b>%s</b>,%s",
+ dna[0], dna[1]);
+ else
+ g_string_append_printf (tmpstring,
+ "<b>%s</b>",
+ dna[0]);
+ }
+ else {
+ gchar *tmp;
+ tmp = g_markup_escape_text (fav->name, -1);
+ g_string_append (tmpstring, tmp);
+ g_free (tmp);
+ }
+ g_strfreev (dna);
+ }
+ else {
+ gchar *tmp;
+ tmp = g_markup_escape_text (fav->name, -1);
+ g_string_append (tmpstring, tmp);
+ g_free (tmp);
+ }
+
+ if (fav->descr && *fav->descr) {
+ gchar *tmp;
+ tmp = g_markup_escape_text (fav->descr, -1);
+ g_string_append_printf (tmpstring,
+ "\n<small>%s</small>", tmp);
+ g_free (tmp);
+
+ g_value_set_string ((av = gda_value_new (G_TYPE_STRING)),
+ fav->descr);
+ gda_tree_node_set_node_attribute (snode, "descr", av, NULL);
+ gda_value_free (av);
+ }
+
+ g_value_take_string ((av = gda_value_new (G_TYPE_STRING)),
+ g_string_free (tmpstring, FALSE));
+ gda_tree_node_set_node_attribute (snode, "markup", av, NULL);
+ gda_value_free (av);
+
+ g_value_set_string ((av = gda_value_new (G_TYPE_STRING)),
+ fav->name);
+ gda_tree_node_set_node_attribute (snode,
+ MGR_FAVORITES_CONTENTS_ATT_NAME,
+ av, NULL);
+ gda_value_free (av);
+ }
+ else if (fav->type == BROWSER_FAVORITES_LDAP_CLASS) {
+ if (!snode) {
+ /* favorite ID */
+ snode = gda_tree_manager_create_node (manager, node, NULL);
+
+ g_value_set_int ((av = gda_value_new (G_TYPE_INT)), fav->id);
+ gda_tree_node_set_node_attribute (snode,
+ MGR_FAVORITES_ID_ATT_NAME,
+ av, NULL);
+ gda_value_free (av);
+
+ /* icon */
+ GdkPixbuf *pixbuf;
+ GdaLdapClass *lcl;
+ lcl = browser_connection_get_class_info (bcnc, fav->name);
+ pixbuf = browser_get_pixbuf_for_ldap_class (lcl ? lcl->kind : GDA_LDAP_CLASS_KIND_UNKNOWN);
+ av = gda_value_new (G_TYPE_OBJECT);
+ g_value_set_object (av, pixbuf);
+ gda_tree_node_set_node_attribute (snode, "icon", av, NULL);
+ gda_value_free (av);
+
+ g_value_set_uint ((av = gda_value_new (G_TYPE_UINT)), fav->type);
+ gda_tree_node_set_node_attribute (snode,
+ MGR_FAVORITES_TYPE_ATT_NAME,
+ av, NULL);
+ gda_value_free (av);
+ }
+
+ GString *tmpstring;
+ gchar *tmp;
+
+ tmpstring = g_string_new ("");
+ tmp = g_markup_escape_text (fav->name, -1);
+ g_string_append (tmpstring, tmp);
+ g_free (tmp);
+
+ if (fav->descr && *fav->descr) {
+ gchar *tmp;
+ tmp = g_markup_escape_text (fav->descr, -1);
+ g_string_append_printf (tmpstring,
+ "\n<small>%s</small>", tmp);
+ g_free (tmp);
+
+ g_value_set_string ((av = gda_value_new (G_TYPE_STRING)),
+ fav->descr);
+ gda_tree_node_set_node_attribute (snode, "descr", av, NULL);
+ gda_value_free (av);
+ }
+
+ g_value_take_string ((av = gda_value_new (G_TYPE_STRING)),
+ g_string_free (tmpstring, FALSE));
+ gda_tree_node_set_node_attribute (snode, "markup", av, NULL);
+ gda_value_free (av);
+
+ g_value_set_string ((av = gda_value_new (G_TYPE_STRING)),
+ fav->name);
+ gda_tree_node_set_node_attribute (snode,
+ MGR_FAVORITES_CONTENTS_ATT_NAME,
+ av, NULL);
+ gda_value_free (av);
+ }
+#endif
else {
TO_IMPLEMENT;
}
@@ -599,3 +784,45 @@ mgr_favorites_update_children (GdaTreeManager *manager, GdaTreeNode *node, const
return g_slist_reverse (nodes_list);
}
+
+static void
+icon_fetched_cb (G_GNUC_UNUSED BrowserConnection *bcnc,
+ GdkPixbuf *pixbuf, GdaTreeNode *node, G_GNUC_UNUSED GError *error)
+{
+ if (pixbuf) {
+ GValue *av;
+ av = gda_value_new (G_TYPE_OBJECT);
+ g_value_set_object (av, pixbuf);
+ gda_tree_node_set_node_attribute (node, "icon", av, NULL);
+ gda_value_free (av);
+ }
+ g_object_unref (node);
+}
+
+#ifdef HAVE_LDAP
+static gboolean
+icons_resol_cb (MgrFavorites *mgr)
+{
+ if (mgr->priv->icons_resol_timer == 0)
+ return FALSE;
+ if (mgr->priv->icons_resol_list) {
+ IconResolutionData *data;
+ data = (IconResolutionData*) mgr->priv->icons_resol_list->data;
+ mgr->priv->icons_resol_list = g_slist_delete_link (mgr->priv->icons_resol_list,
+ mgr->priv->icons_resol_list);
+
+ if (browser_connection_ldap_icon_for_dn (mgr->priv->bcnc, data->dn,
+ (BrowserConnectionJobCallback) icon_fetched_cb,
+ g_object_ref (data->node), NULL) == 0)
+ g_object_unref (data->node);
+ icon_resolution_data_free (data);
+ }
+
+ if (! mgr->priv->icons_resol_list) {
+ mgr->priv->icons_resol_timer = 0;
+ return FALSE;
+ }
+ else
+ return TRUE;
+}
+#endif
diff --git a/tools/browser/query-exec/Makefile.am b/tools/browser/query-exec/Makefile.am
index 09020b4..2104d91 100644
--- a/tools/browser/query-exec/Makefile.am
+++ b/tools/browser/query-exec/Makefile.am
@@ -19,8 +19,8 @@ marshal.c: marshal.list $(GLIB_GENMARSHAL) marshal.h
libperspective_la_SOURCES = \
marshal.c \
marshal.h \
- query-console.c \
- query-console.h \
+ query-console-page.c \
+ query-console-page.h \
query-editor.c \
query-editor.h \
query-favorite-selector.c \
diff --git a/tools/browser/query-exec/query-console.c b/tools/browser/query-exec/query-console-page.c
similarity index 90%
rename from tools/browser/query-exec/query-console.c
rename to tools/browser/query-exec/query-console-page.c
index 0ca600e..c0313d5 100644
--- a/tools/browser/query-exec/query-console.c
+++ b/tools/browser/query-exec/query-console-page.c
@@ -22,7 +22,7 @@
#include <glib/gi18n-lib.h>
#include <string.h>
-#include "query-console.h"
+#include "query-console-page.h"
#include "../dnd.h"
#include "../support.h"
#include "../cc-gray-bar.h"
@@ -93,7 +93,7 @@ execution_statement_free (ExecutionStatement *estmt)
}
}
-struct _QueryConsolePrivate {
+struct _QueryConsolePagePrivate {
BrowserConnection *bcnc;
GdaSqlParser *parser;
@@ -127,40 +127,40 @@ struct _QueryConsolePrivate {
GtkWidget *favorites_menu;
};
-static void query_console_class_init (QueryConsoleClass *klass);
-static void query_console_init (QueryConsole *tconsole, QueryConsoleClass *klass);
-static void query_console_dispose (GObject *object);
-static void query_console_show_all (GtkWidget *widget);
-static void query_console_grab_focus (GtkWidget *widget);
+static void query_console_page_class_init (QueryConsolePageClass *klass);
+static void query_console_page_init (QueryConsolePage *tconsole, QueryConsolePageClass *klass);
+static void query_console_page_dispose (GObject *object);
+static void query_console_page_show_all (GtkWidget *widget);
+static void query_console_page_grab_focus (GtkWidget *widget);
/* BrowserPage interface */
-static void query_console_page_init (BrowserPageIface *iface);
-static GtkActionGroup *query_console_page_get_actions_group (BrowserPage *page);
-static const gchar *query_console_page_get_actions_ui (BrowserPage *page);
-static GtkWidget *query_console_page_get_tab_label (BrowserPage *page, GtkWidget **out_close_button);
+static void query_console_page_page_init (BrowserPageIface *iface);
+static GtkActionGroup *query_console_page_page_get_actions_group (BrowserPage *page);
+static const gchar *query_console_page_page_get_actions_ui (BrowserPage *page);
+static GtkWidget *query_console_page_page_get_tab_label (BrowserPage *page, GtkWidget **out_close_button);
static GObjectClass *parent_class = NULL;
/*
- * QueryConsole class implementation
+ * QueryConsolePage class implementation
*/
static void
-query_console_class_init (QueryConsoleClass *klass)
+query_console_page_class_init (QueryConsolePageClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
parent_class = g_type_class_peek_parent (klass);
- object_class->dispose = query_console_dispose;
- GTK_WIDGET_CLASS (klass)->show_all = query_console_show_all;
- GTK_WIDGET_CLASS (klass)->grab_focus = query_console_grab_focus;
+ object_class->dispose = query_console_page_dispose;
+ GTK_WIDGET_CLASS (klass)->show_all = query_console_page_show_all;
+ GTK_WIDGET_CLASS (klass)->grab_focus = query_console_page_grab_focus;
}
static void
-query_console_show_all (GtkWidget *widget)
+query_console_page_show_all (GtkWidget *widget)
{
- QueryConsole *tconsole = (QueryConsole *) widget;
+ QueryConsolePage *tconsole = (QueryConsolePage *) widget;
GTK_WIDGET_CLASS (parent_class)->show_all (widget);
if (gtk_toggle_button_get_active (tconsole->priv->params_toggle))
@@ -170,17 +170,17 @@ query_console_show_all (GtkWidget *widget)
}
static void
-query_console_page_init (BrowserPageIface *iface)
+query_console_page_page_init (BrowserPageIface *iface)
{
- iface->i_get_actions_group = query_console_page_get_actions_group;
- iface->i_get_actions_ui = query_console_page_get_actions_ui;
- iface->i_get_tab_label = query_console_page_get_tab_label;
+ iface->i_get_actions_group = query_console_page_page_get_actions_group;
+ iface->i_get_actions_ui = query_console_page_page_get_actions_ui;
+ iface->i_get_tab_label = query_console_page_page_get_tab_label;
}
static void
-query_console_init (QueryConsole *tconsole, G_GNUC_UNUSED QueryConsoleClass *klass)
+query_console_page_init (QueryConsolePage *tconsole, G_GNUC_UNUSED QueryConsolePageClass *klass)
{
- tconsole->priv = g_new0 (QueryConsolePrivate, 1);
+ tconsole->priv = g_new0 (QueryConsolePagePrivate, 1);
tconsole->priv->parser = NULL;
tconsole->priv->params_compute_id = 0;
tconsole->priv->params = NULL;
@@ -189,11 +189,11 @@ query_console_init (QueryConsole *tconsole, G_GNUC_UNUSED QueryConsoleClass *kla
tconsole->priv->fav_id = -1;
}
static void connection_busy_cb (BrowserConnection *bcnc, gboolean is_busy,
- gchar *reason, QueryConsole *tconsole);
+ gchar *reason, QueryConsolePage *tconsole);
static void
-query_console_dispose (GObject *object)
+query_console_page_dispose (GObject *object)
{
- QueryConsole *tconsole = (QueryConsole *) object;
+ QueryConsolePage *tconsole = (QueryConsolePage *) object;
/* free memory */
if (tconsole->priv) {
@@ -227,60 +227,60 @@ query_console_dispose (GObject *object)
}
GType
-query_console_get_type (void)
+query_console_page_get_type (void)
{
static GType type = 0;
if (G_UNLIKELY (type == 0)) {
static const GTypeInfo console = {
- sizeof (QueryConsoleClass),
+ sizeof (QueryConsolePageClass),
(GBaseInitFunc) NULL,
(GBaseFinalizeFunc) NULL,
- (GClassInitFunc) query_console_class_init,
+ (GClassInitFunc) query_console_page_class_init,
NULL,
NULL,
- sizeof (QueryConsole),
+ sizeof (QueryConsolePage),
0,
- (GInstanceInitFunc) query_console_init,
+ (GInstanceInitFunc) query_console_page_init,
0
};
static GInterfaceInfo page_console = {
- (GInterfaceInitFunc) query_console_page_init,
+ (GInterfaceInitFunc) query_console_page_page_init,
NULL,
NULL
};
- type = g_type_register_static (GTK_TYPE_VBOX, "QueryConsole", &console, 0);
+ type = g_type_register_static (GTK_TYPE_VBOX, "QueryConsolePage", &console, 0);
g_type_add_interface_static (type, BROWSER_PAGE_TYPE, &page_console);
}
return type;
}
-static void editor_changed_cb (QueryEditor *editor, QueryConsole *tconsole);
-static void editor_execute_request_cb (QueryEditor *editor, QueryConsole *tconsole);
-static void sql_clear_clicked_cb (GtkButton *button, QueryConsole *tconsole);
-static void sql_variables_clicked_cb (GtkToggleButton *button, QueryConsole *tconsole);
-static void sql_execute_clicked_cb (GtkButton *button, QueryConsole *tconsole);
-static void sql_indent_clicked_cb (GtkButton *button, QueryConsole *tconsole);
-static void sql_favorite_clicked_cb (GtkButton *button, QueryConsole *tconsole);
-
-static void history_copy_clicked_cb (GtkButton *button, QueryConsole *tconsole);
-static void history_clear_clicked_cb (GtkButton *button, QueryConsole *tconsole);
-static void history_changed_cb (QueryEditor *history, QueryConsole *tconsole);
+static void editor_changed_cb (QueryEditor *editor, QueryConsolePage *tconsole);
+static void editor_execute_request_cb (QueryEditor *editor, QueryConsolePage *tconsole);
+static void sql_clear_clicked_cb (GtkButton *button, QueryConsolePage *tconsole);
+static void sql_variables_clicked_cb (GtkToggleButton *button, QueryConsolePage *tconsole);
+static void sql_execute_clicked_cb (GtkButton *button, QueryConsolePage *tconsole);
+static void sql_indent_clicked_cb (GtkButton *button, QueryConsolePage *tconsole);
+static void sql_favorite_clicked_cb (GtkButton *button, QueryConsolePage *tconsole);
+
+static void history_copy_clicked_cb (GtkButton *button, QueryConsolePage *tconsole);
+static void history_clear_clicked_cb (GtkButton *button, QueryConsolePage *tconsole);
+static void history_changed_cb (QueryEditor *history, QueryConsolePage *tconsole);
/**
- * query_console_new
+ * query_console_page_new
*
* Returns: a new #GtkWidget
*/
GtkWidget *
-query_console_new (BrowserConnection *bcnc)
+query_console_page_new (BrowserConnection *bcnc)
{
- QueryConsole *tconsole;
+ QueryConsolePage *tconsole;
g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), NULL);
- tconsole = QUERY_CONSOLE (g_object_new (QUERY_CONSOLE_TYPE, NULL));
+ tconsole = QUERY_CONSOLE_PAGE (g_object_new (QUERY_CONSOLE_PAGE_TYPE, NULL));
tconsole->priv->bcnc = g_object_ref (bcnc);
@@ -470,7 +470,7 @@ query_console_new (BrowserConnection *bcnc)
}
static void
-connection_busy_cb (G_GNUC_UNUSED BrowserConnection *bcnc, gboolean is_busy, G_GNUC_UNUSED gchar *reason, QueryConsole *tconsole)
+connection_busy_cb (G_GNUC_UNUSED BrowserConnection *bcnc, gboolean is_busy, G_GNUC_UNUSED gchar *reason, QueryConsolePage *tconsole)
{
gtk_widget_set_sensitive (tconsole->priv->exec_button, !is_busy);
gtk_widget_set_sensitive (tconsole->priv->indent_button, !is_busy);
@@ -483,7 +483,7 @@ connection_busy_cb (G_GNUC_UNUSED BrowserConnection *bcnc, gboolean is_busy, G_G
}
static void
-history_changed_cb (G_GNUC_UNUSED QueryEditor *history, QueryConsole *tconsole)
+history_changed_cb (G_GNUC_UNUSED QueryEditor *history, QueryConsolePage *tconsole)
{
gboolean act = FALSE;
QueryEditor *qe;
@@ -507,13 +507,13 @@ history_changed_cb (G_GNUC_UNUSED QueryEditor *history, QueryConsole *tconsole)
}
static void
-history_clear_clicked_cb (G_GNUC_UNUSED GtkButton *button, QueryConsole *tconsole)
+history_clear_clicked_cb (G_GNUC_UNUSED GtkButton *button, QueryConsolePage *tconsole)
{
query_editor_del_all_history_items (tconsole->priv->history);
}
static void
-history_copy_clicked_cb (G_GNUC_UNUSED GtkButton *button, QueryConsole *tconsole)
+history_copy_clicked_cb (G_GNUC_UNUSED GtkButton *button, QueryConsolePage *tconsole)
{
QueryEditorHistoryItem *qih;
QueryEditor *qe;
@@ -544,7 +544,7 @@ history_copy_clicked_cb (G_GNUC_UNUSED GtkButton *button, QueryConsole *tconsole
static gboolean
-compute_params (QueryConsole *tconsole)
+compute_params (QueryConsolePage *tconsole)
{
gchar *sql;
GdaBatch *batch;
@@ -614,7 +614,7 @@ compute_params (QueryConsole *tconsole)
}
static void
-editor_changed_cb (G_GNUC_UNUSED QueryEditor *editor, QueryConsole *tconsole)
+editor_changed_cb (G_GNUC_UNUSED QueryEditor *editor, QueryConsolePage *tconsole)
{
if (tconsole->priv->params_compute_id)
g_source_remove (tconsole->priv->params_compute_id);
@@ -622,7 +622,7 @@ editor_changed_cb (G_GNUC_UNUSED QueryEditor *editor, QueryConsole *tconsole)
}
static void
-editor_execute_request_cb (G_GNUC_UNUSED QueryEditor *editor, QueryConsole *tconsole)
+editor_execute_request_cb (G_GNUC_UNUSED QueryEditor *editor, QueryConsolePage *tconsole)
{
gboolean sensitive;
g_object_get (tconsole->priv->exec_button, "sensitive", &sensitive, NULL);
@@ -631,7 +631,7 @@ editor_execute_request_cb (G_GNUC_UNUSED QueryEditor *editor, QueryConsole *tcon
}
static void
-sql_variables_clicked_cb (GtkToggleButton *button, QueryConsole *tconsole)
+sql_variables_clicked_cb (GtkToggleButton *button, QueryConsolePage *tconsole)
{
if (gtk_toggle_button_get_active (button))
gtk_widget_show (tconsole->priv->params_top);
@@ -640,7 +640,7 @@ sql_variables_clicked_cb (GtkToggleButton *button, QueryConsole *tconsole)
}
static void
-sql_clear_clicked_cb (G_GNUC_UNUSED GtkButton *button, QueryConsole *tconsole)
+sql_clear_clicked_cb (G_GNUC_UNUSED GtkButton *button, QueryConsolePage *tconsole)
{
query_editor_set_text (tconsole->priv->editor, NULL);
tconsole->priv->fav_id = -1;
@@ -648,7 +648,7 @@ sql_clear_clicked_cb (G_GNUC_UNUSED GtkButton *button, QueryConsole *tconsole)
}
static void
-sql_indent_clicked_cb (G_GNUC_UNUSED GtkButton *button, QueryConsole *tconsole)
+sql_indent_clicked_cb (G_GNUC_UNUSED GtkButton *button, QueryConsolePage *tconsole)
{
gchar *sql;
GdaBatch *batch;
@@ -682,11 +682,11 @@ sql_indent_clicked_cb (G_GNUC_UNUSED GtkButton *button, QueryConsole *tconsole)
}
}
-static void sql_favorite_new_mitem_cb (G_GNUC_UNUSED GtkMenuItem *mitem, QueryConsole *tconsole);
-static void sql_favorite_modify_mitem_cb (G_GNUC_UNUSED GtkMenuItem *mitem, QueryConsole *tconsole);
+static void sql_favorite_new_mitem_cb (G_GNUC_UNUSED GtkMenuItem *mitem, QueryConsolePage *tconsole);
+static void sql_favorite_modify_mitem_cb (G_GNUC_UNUSED GtkMenuItem *mitem, QueryConsolePage *tconsole);
static void
-sql_favorite_clicked_cb (G_GNUC_UNUSED GtkButton *button, QueryConsole *tconsole)
+sql_favorite_clicked_cb (G_GNUC_UNUSED GtkButton *button, QueryConsolePage *tconsole)
{
GtkWidget *menu, *mitem;
BrowserFavorites *bfav;
@@ -761,7 +761,7 @@ sql_favorite_clicked_cb (G_GNUC_UNUSED GtkButton *button, QueryConsole *tconsole
}
static void
-sql_favorite_new_mitem_cb (GtkMenuItem *mitem, QueryConsole *tconsole)
+sql_favorite_new_mitem_cb (GtkMenuItem *mitem, QueryConsolePage *tconsole)
{
BrowserFavorites *bfav;
BrowserFavoritesAttributes fav;
@@ -809,7 +809,7 @@ sql_favorite_new_mitem_cb (GtkMenuItem *mitem, QueryConsole *tconsole)
}
static void
-sql_favorite_modify_mitem_cb (G_GNUC_UNUSED GtkMenuItem *mitem, QueryConsole *tconsole)
+sql_favorite_modify_mitem_cb (G_GNUC_UNUSED GtkMenuItem *mitem, QueryConsolePage *tconsole)
{
BrowserFavorites *bfav;
BrowserFavoritesAttributes fav;
@@ -861,7 +861,7 @@ popup_container_position_func (PopupContainer *cont, gint *out_x, gint *out_y)
static void
params_form_changed_cb (GdauiBasicForm *form, G_GNUC_UNUSED GdaHolder *param,
- G_GNUC_UNUSED gboolean is_user_modif, QueryConsole *tconsole)
+ G_GNUC_UNUSED gboolean is_user_modif, QueryConsolePage *tconsole)
{
/* if all params are valid => authorize the execute button */
GtkWidget *button;
@@ -871,10 +871,10 @@ params_form_changed_cb (GdauiBasicForm *form, G_GNUC_UNUSED GdaHolder *param,
gdaui_basic_form_is_valid (form));
}
-static gboolean query_exec_fetch_cb (QueryConsole *tconsole);
+static gboolean query_exec_fetch_cb (QueryConsolePage *tconsole);
static void
-sql_execute_clicked_cb (G_GNUC_UNUSED GtkButton *button, QueryConsole *tconsole)
+sql_execute_clicked_cb (G_GNUC_UNUSED GtkButton *button, QueryConsolePage *tconsole)
{
gchar *sql;
const gchar *remain;
@@ -1019,7 +1019,7 @@ sql_execute_clicked_cb (G_GNUC_UNUSED GtkButton *button, QueryConsole *tconsole)
}
static gboolean
-query_exec_fetch_cb (QueryConsole *tconsole)
+query_exec_fetch_cb (QueryConsolePage *tconsole)
{
gboolean alldone = TRUE;
@@ -1067,7 +1067,7 @@ query_exec_fetch_cb (QueryConsole *tconsole)
}
else
browser_window_push_status (BROWSER_WINDOW (gtk_widget_get_toplevel ((GtkWidget*) tconsole)),
- "QueryConsole", _("Statement executed"), TRUE);
+ "QueryConsolePage", _("Statement executed"), TRUE);
/* display a message if a transaction has been started */
if (! history->within_transaction &&
@@ -1133,17 +1133,17 @@ query_exec_fetch_cb (QueryConsole *tconsole)
}
/**
- * query_console_set_text
- * @console: a #QueryConsole
+ * query_console_page_set_text
+ * @console: a #QueryConsolePage
* @text: the new text
* @fav_id: the favorite ID or -1 if not a favorite.
*
* Replaces the edited SQL with @text in @console
*/
void
-query_console_set_text (QueryConsole *console, const gchar *text, gint fav_id)
+query_console_page_set_text (QueryConsolePage *console, const gchar *text, gint fav_id)
{
- g_return_if_fail (IS_QUERY_CONSOLE (console));
+ g_return_if_fail (IS_QUERY_CONSOLE_PAGE_PAGE (console));
console->priv->fav_id = fav_id;
query_editor_set_text (console->priv->editor, text);
}
@@ -1152,14 +1152,14 @@ query_console_set_text (QueryConsole *console, const gchar *text, gint fav_id)
* UI actions
*/
static void
-query_execute_cb (G_GNUC_UNUSED GtkAction *action, QueryConsole *tconsole)
+query_execute_cb (G_GNUC_UNUSED GtkAction *action, QueryConsolePage *tconsole)
{
sql_execute_clicked_cb (NULL, tconsole);
}
#ifdef HAVE_GTKSOURCEVIEW
static void
-editor_undo_cb (G_GNUC_UNUSED GtkAction *action, G_GNUC_UNUSED QueryConsole *tconsole)
+editor_undo_cb (G_GNUC_UNUSED GtkAction *action, G_GNUC_UNUSED QueryConsolePage *tconsole)
{
TO_IMPLEMENT;
}
@@ -1187,10 +1187,10 @@ static const gchar *ui_actions_console =
"</ui>";
static GtkActionGroup *
-query_console_page_get_actions_group (BrowserPage *page)
+query_console_page_page_get_actions_group (BrowserPage *page)
{
- QueryConsole *tconsole;
- tconsole = QUERY_CONSOLE (page);
+ QueryConsolePage *tconsole;
+ tconsole = QUERY_CONSOLE_PAGE (page);
if (! tconsole->priv->agroup) {
tconsole->priv->agroup = gtk_action_group_new ("QueryExecConsoleActions");
gtk_action_group_set_translation_domain (tconsole->priv->agroup, GETTEXT_PACKAGE);
@@ -1205,18 +1205,18 @@ query_console_page_get_actions_group (BrowserPage *page)
}
static const gchar *
-query_console_page_get_actions_ui (G_GNUC_UNUSED BrowserPage *page)
+query_console_page_page_get_actions_ui (G_GNUC_UNUSED BrowserPage *page)
{
return ui_actions_console;
}
static GtkWidget *
-query_console_page_get_tab_label (BrowserPage *page, GtkWidget **out_close_button)
+query_console_page_page_get_tab_label (BrowserPage *page, GtkWidget **out_close_button)
{
- QueryConsole *tconsole;
+ QueryConsolePage *tconsole;
const gchar *tab_name;
- tconsole = QUERY_CONSOLE (page);
+ tconsole = QUERY_CONSOLE_PAGE (page);
tab_name = _("Query editor");
return browser_make_tab_label_with_stock (tab_name,
STOCK_CONSOLE,
@@ -1224,10 +1224,10 @@ query_console_page_get_tab_label (BrowserPage *page, GtkWidget **out_close_butto
}
static void
-query_console_grab_focus (GtkWidget *widget)
+query_console_page_grab_focus (GtkWidget *widget)
{
- QueryConsole *tconsole;
+ QueryConsolePage *tconsole;
- tconsole = QUERY_CONSOLE (widget);
+ tconsole = QUERY_CONSOLE_PAGE (widget);
gtk_widget_grab_focus (GTK_WIDGET (tconsole->priv->editor));
}
diff --git a/tools/browser/query-exec/query-console-page.h b/tools/browser/query-exec/query-console-page.h
new file mode 100644
index 0000000..032830a
--- /dev/null
+++ b/tools/browser/query-exec/query-console-page.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2009 - 2011 The GNOME Foundation
+ *
+ * AUTHORS:
+ * Vivien Malerba <malerba gnome-db org>
+ *
+ * This Program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __QUERY_CONSOLE_PAGE_H__
+#define __QUERY_CONSOLE_PAGE_H__
+
+#include <gtk/gtk.h>
+#include "../browser-connection.h"
+
+G_BEGIN_DECLS
+
+#define QUERY_CONSOLE_PAGE_TYPE (query_console_page_get_type())
+#define QUERY_CONSOLE_PAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, QUERY_CONSOLE_PAGE_TYPE, QueryConsolePage))
+#define QUERY_CONSOLE_PAGE_PAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, QUERY_CONSOLE_PAGE_TYPE, QueryConsolePageClass))
+#define IS_QUERY_CONSOLE_PAGE_PAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, QUERY_CONSOLE_PAGE_TYPE))
+#define IS_QUERY_CONSOLE_PAGE_PAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), QUERY_CONSOLE_PAGE_TYPE))
+
+typedef struct _QueryConsolePage QueryConsolePage;
+typedef struct _QueryConsolePageClass QueryConsolePageClass;
+typedef struct _QueryConsolePagePrivate QueryConsolePagePrivate;
+
+struct _QueryConsolePage {
+ GtkVBox parent;
+ QueryConsolePagePrivate *priv;
+};
+
+struct _QueryConsolePageClass {
+ GtkVBoxClass parent_class;
+};
+
+GType query_console_page_get_type (void) G_GNUC_CONST;
+
+GtkWidget *query_console_page_new (BrowserConnection *bcnc);
+void query_console_page_set_text (QueryConsolePage *console, const gchar *text, gint fav_id);
+
+G_END_DECLS
+
+#endif
diff --git a/tools/browser/query-exec/query-exec-perspective.c b/tools/browser/query-exec/query-exec-perspective.c
index 05cfa3b..fb7b3aa 100644
--- a/tools/browser/query-exec/query-exec-perspective.c
+++ b/tools/browser/query-exec/query-exec-perspective.c
@@ -22,7 +22,7 @@
#include "query-exec-perspective.h"
#include "../browser-window.h"
#include "../browser-page.h"
-#include "query-console.h"
+#include "query-console-page.h"
#include "../browser-stock-icons.h"
#include "../support.h"
#include "query-favorite-selector.h"
@@ -39,6 +39,7 @@ static void query_exec_perspective_grab_focus (GtkWidget *widget);
/* BrowserPerspective interface */
static void query_exec_perspective_perspective_init (BrowserPerspectiveIface *iface);
+static BrowserWindow *query_exec_perspective_get_window (BrowserPerspective *perspective);
static GtkActionGroup *query_exec_perspective_get_actions_group (BrowserPerspective *perspective);
static const gchar *query_exec_perspective_get_actions_ui (BrowserPerspective *perspective);
static void query_exec_perspective_get_current_customization (BrowserPerspective *perspective,
@@ -46,7 +47,6 @@ static void query_exec_perspective_get_current_customization (Br
const gchar **out_ui);
static void query_exec_perspective_page_tab_label_change (BrowserPerspective *perspective, BrowserPage *page);
-static void adapt_notebook_for_fullscreen (QueryExecPerspective *perspective);
/* get a pointer to the parents to be able to call their destructor */
static GObjectClass *parent_class = NULL;
@@ -57,9 +57,6 @@ struct _QueryExecPerspectivePrivate {
gboolean favorites_shown;
BrowserWindow *bwin;
BrowserConnection *bcnc;
-
- GtkActionGroup *action_group;
- gboolean fullscreen;
};
GType
@@ -117,10 +114,10 @@ query_exec_perspective_grab_focus (GtkWidget *widget)
gtk_widget_grab_focus (gtk_notebook_get_nth_page (nb,
gtk_notebook_get_current_page (nb)));
}
-
static void
query_exec_perspective_perspective_init (BrowserPerspectiveIface *iface)
{
+ iface->i_get_window = query_exec_perspective_get_window;
iface->i_get_actions_group = query_exec_perspective_get_actions_group;
iface->i_get_actions_ui = query_exec_perspective_get_actions_ui;
iface->i_get_current_customization = query_exec_perspective_get_current_customization;
@@ -131,21 +128,12 @@ static void
query_exec_perspective_init (QueryExecPerspective *perspective)
{
perspective->priv = g_new0 (QueryExecPerspectivePrivate, 1);
- perspective->priv->action_group = NULL;
perspective->priv->favorites_shown = TRUE;
- perspective->priv->fullscreen = FALSE;
}
static void fav_selection_changed_cb (GtkWidget *widget, gint fav_id, BrowserFavoritesType fav_type,
const gchar *selection, QueryExecPerspective *perspective);
-static void nb_switch_page_cb (GtkNotebook *nb, GtkWidget *page, gint page_num,
- QueryExecPerspective *perspective);
-static void nb_page_removed_cb (GtkNotebook *nb, GtkWidget *page, gint page_num,
- QueryExecPerspective *perspective);
-static void nb_page_added_cb (GtkNotebook *nb, GtkWidget *page, gint page_num,
- QueryExecPerspective *perspective);
static void close_button_clicked_cb (GtkWidget *wid, GtkWidget *page_widget);
-static void fullscreen_changed_cb (BrowserWindow *bwin, gboolean fullscreen, QueryExecPerspective *perspective);
/**
* query_exec_perspective_new
@@ -158,26 +146,27 @@ query_exec_perspective_new (BrowserWindow *bwin)
BrowserConnection *bcnc;
BrowserPerspective *bpers;
QueryExecPerspective *perspective;
+ gboolean fav_supported;
bpers = (BrowserPerspective*) g_object_new (TYPE_QUERY_EXEC_PERSPECTIVE, NULL);
perspective = (QueryExecPerspective*) bpers;
perspective->priv->bwin = bwin;
- g_signal_connect (bwin, "fullscreen-changed",
- G_CALLBACK (fullscreen_changed_cb), bpers);
bcnc = browser_window_get_connection (bwin);
perspective->priv->bcnc = g_object_ref (bcnc);
- perspective->priv->fullscreen = browser_window_is_fullscreen (bwin);
+ fav_supported = browser_connection_get_favorites (bcnc) ? TRUE : FALSE;
/* contents */
GtkWidget *paned, *nb, *wid;
paned = gtk_hpaned_new ();
- wid = query_favorite_selector_new (bcnc);
- g_signal_connect (wid, "selection-changed",
- G_CALLBACK (fav_selection_changed_cb), bpers);
- gtk_paned_pack1 (GTK_PANED (paned), wid, FALSE, TRUE);
- gtk_paned_set_position (GTK_PANED (paned), DEFAULT_FAVORITES_SIZE);
- perspective->priv->favorites = wid;
+ if (fav_supported) {
+ wid = query_favorite_selector_new (bcnc);
+ g_signal_connect (wid, "selection-changed",
+ G_CALLBACK (fav_selection_changed_cb), bpers);
+ gtk_paned_pack1 (GTK_PANED (paned), wid, FALSE, TRUE);
+ gtk_paned_set_position (GTK_PANED (paned), DEFAULT_FAVORITES_SIZE);
+ perspective->priv->favorites = wid;
+ }
nb = gtk_notebook_new ();
perspective->priv->notebook = nb;
@@ -187,7 +176,7 @@ query_exec_perspective_new (BrowserWindow *bwin)
GtkWidget *page, *tlabel, *button;
- page = query_console_new (bcnc);
+ page = query_console_page_new (bcnc);
tlabel = browser_page_get_tab_label (BROWSER_PAGE (page), &button);
g_signal_connect (button, "clicked",
G_CALLBACK (close_button_clicked_cb), page);
@@ -205,20 +194,11 @@ query_exec_perspective_new (BrowserWindow *bwin)
gtk_box_pack_start (GTK_BOX (bpers), paned, TRUE, TRUE, 0);
gtk_widget_show_all (paned);
- if (!perspective->priv->favorites_shown)
+ if (perspective->priv->favorites && !perspective->priv->favorites_shown)
gtk_widget_hide (perspective->priv->favorites);
gtk_widget_grab_focus (page);
- /* signals to customize perspective */
- g_signal_connect (G_OBJECT (nb), "switch-page",
- G_CALLBACK (nb_switch_page_cb), perspective);
- g_signal_connect (G_OBJECT (nb), "page-removed",
- G_CALLBACK (nb_page_removed_cb), perspective);
- g_signal_connect (G_OBJECT (nb), "page-added",
- G_CALLBACK (nb_page_added_cb), perspective);
-
- if (perspective->priv->fullscreen)
- adapt_notebook_for_fullscreen (perspective);
+ browser_perspective_declare_notebook (bpers, GTK_NOTEBOOK (perspective->priv->notebook));
return bpers;
}
@@ -235,8 +215,8 @@ fav_selection_changed_cb (G_GNUC_UNUSED GtkWidget *widget, gint fav_id,
page = gtk_notebook_get_nth_page (nb, gtk_notebook_get_current_page (nb));
if (!page)
return;
- if (IS_QUERY_CONSOLE (page)) {
- query_console_set_text (QUERY_CONSOLE (page), selection, fav_id);
+ if (IS_QUERY_CONSOLE_PAGE_PAGE (page)) {
+ query_console_page_set_text (QUERY_CONSOLE_PAGE (page), selection, fav_id);
gtk_widget_grab_focus (page);
}
else {
@@ -245,67 +225,11 @@ fav_selection_changed_cb (G_GNUC_UNUSED GtkWidget *widget, gint fav_id,
}
static void
-nb_switch_page_cb (GtkNotebook *nb, G_GNUC_UNUSED GtkWidget *page, gint page_num,
- QueryExecPerspective *perspective)
-{
- GtkWidget *page_contents;
- GtkActionGroup *actions = NULL;
- const gchar *ui = NULL;
-
- page_contents = gtk_notebook_get_nth_page (nb, page_num);
- if (IS_BROWSER_PAGE (page_contents)) {
- actions = browser_page_get_actions_group (BROWSER_PAGE (page_contents));
- ui = browser_page_get_actions_ui (BROWSER_PAGE (page_contents));
- }
- browser_window_customize_perspective_ui (perspective->priv->bwin,
- BROWSER_PERSPECTIVE (perspective), actions,
- ui);
- if (actions)
- g_object_unref (actions);
-}
-
-static void
-nb_page_removed_cb (GtkNotebook *nb, G_GNUC_UNUSED GtkWidget *page, G_GNUC_UNUSED gint page_num,
- QueryExecPerspective *perspective)
-{
- if (gtk_notebook_get_n_pages (nb) == 0) {
- browser_window_customize_perspective_ui (perspective->priv->bwin,
- BROWSER_PERSPECTIVE (perspective),
- NULL, NULL);
- }
- adapt_notebook_for_fullscreen (perspective);
-}
-
-static void
-nb_page_added_cb (G_GNUC_UNUSED GtkNotebook *nb, G_GNUC_UNUSED GtkWidget *page,
- G_GNUC_UNUSED gint page_num, QueryExecPerspective *perspective)
-{
- adapt_notebook_for_fullscreen (perspective);
-}
-
-static void
close_button_clicked_cb (G_GNUC_UNUSED GtkWidget *wid, GtkWidget *page_widget)
{
gtk_widget_destroy (page_widget);
}
-static void
-adapt_notebook_for_fullscreen (QueryExecPerspective *perspective)
-{
- gboolean showtabs = TRUE;
-
- if (perspective->priv->fullscreen &&
- gtk_notebook_get_n_pages (GTK_NOTEBOOK (perspective->priv->notebook)) == 1)
- showtabs = FALSE;
- gtk_notebook_set_show_tabs (GTK_NOTEBOOK (perspective->priv->notebook), showtabs);
-}
-
-static void
-fullscreen_changed_cb (G_GNUC_UNUSED BrowserWindow *bwin, gboolean fullscreen, QueryExecPerspective *perspective)
-{
- perspective->priv->fullscreen = fullscreen;
- adapt_notebook_for_fullscreen (perspective);
-}
static void
query_exec_perspective_dispose (GObject *object)
@@ -317,18 +241,10 @@ query_exec_perspective_dispose (GObject *object)
perspective = QUERY_EXEC_PERSPECTIVE (object);
if (perspective->priv) {
+ browser_perspective_declare_notebook ((BrowserPerspective*) perspective, NULL);
if (perspective->priv->bcnc)
g_object_unref (perspective->priv->bcnc);
- if (perspective->priv->action_group)
- g_object_unref (perspective->priv->action_group);
-
- g_signal_handlers_disconnect_by_func (perspective->priv->notebook,
- G_CALLBACK (nb_page_removed_cb), perspective);
- g_signal_handlers_disconnect_by_func (perspective->priv->notebook,
- G_CALLBACK (nb_page_added_cb), perspective);
- g_signal_handlers_disconnect_by_func (perspective->priv->notebook,
- G_CALLBACK (nb_switch_page_cb), perspective);
g_free (perspective->priv);
perspective->priv = NULL;
}
@@ -348,7 +264,7 @@ query_exec_add_cb (G_GNUC_UNUSED GtkAction *action, BrowserPerspective *bpers)
perspective = QUERY_EXEC_PERSPECTIVE (bpers);
bcnc = perspective->priv->bcnc;
- page = query_console_new (bcnc);
+ page = query_console_page_new (bcnc);
gtk_widget_show (page);
tlabel = browser_page_get_tab_label (BROWSER_PAGE (page), &button);
g_signal_connect (button, "clicked",
@@ -366,8 +282,6 @@ query_exec_add_cb (G_GNUC_UNUSED GtkAction *action, BrowserPerspective *bpers)
gtk_notebook_set_menu_label (GTK_NOTEBOOK (perspective->priv->notebook), page, tlabel);
gtk_widget_grab_focus (page);
-
- adapt_notebook_for_fullscreen (perspective);
}
static void
@@ -375,6 +289,9 @@ favorites_toggle_cb (GtkToggleAction *action, BrowserPerspective *bpers)
{
QueryExecPerspective *perspective;
perspective = QUERY_EXEC_PERSPECTIVE (bpers);
+ if (!perspective->priv->favorites)
+ return;
+
perspective->priv->favorites_shown = gtk_toggle_action_get_active (action);
if (perspective->priv->favorites_shown)
gtk_widget_show (perspective->priv->favorites);
@@ -415,24 +332,24 @@ static GtkActionGroup *
query_exec_perspective_get_actions_group (BrowserPerspective *perspective)
{
QueryExecPerspective *bpers;
+ GtkActionGroup *agroup;
bpers = QUERY_EXEC_PERSPECTIVE (perspective);
-
- if (!bpers->priv->action_group) {
- GtkActionGroup *agroup;
- agroup = gtk_action_group_new ("QueryExecActions");
- gtk_action_group_set_translation_domain (agroup, GETTEXT_PACKAGE);
- gtk_action_group_add_actions (agroup, ui_actions, G_N_ELEMENTS (ui_actions), bpers);
- bpers->priv->action_group = g_object_ref (agroup);
-
- gtk_action_group_add_toggle_actions (agroup, ui_toggle_actions, G_N_ELEMENTS (ui_toggle_actions),
- bpers);
- GtkAction *action;
- action = gtk_action_group_get_action (agroup, "QueryExecFavoritesShow");
+ agroup = gtk_action_group_new ("QueryExecActions");
+ gtk_action_group_set_translation_domain (agroup, GETTEXT_PACKAGE);
+ gtk_action_group_add_actions (agroup, ui_actions, G_N_ELEMENTS (ui_actions), bpers);
+
+ gtk_action_group_add_toggle_actions (agroup, ui_toggle_actions,
+ G_N_ELEMENTS (ui_toggle_actions),
+ bpers);
+ GtkAction *action;
+ action = gtk_action_group_get_action (agroup, "QueryExecFavoritesShow");
+ if (bpers->priv->favorites)
gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
- QUERY_EXEC_PERSPECTIVE (bpers)->priv->favorites_shown);
- }
+ bpers->priv->favorites_shown);
+ else
+ gtk_action_set_sensitive (GTK_ACTION (action), FALSE);
- return bpers->priv->action_group;
+ return agroup;
}
static const gchar *
@@ -478,3 +395,11 @@ query_exec_perspective_get_current_customization (BrowserPerspective *perspectiv
*out_ui = browser_page_get_actions_ui (BROWSER_PAGE (page_contents));
}
}
+
+static BrowserWindow *
+query_exec_perspective_get_window (BrowserPerspective *perspective)
+{
+ QueryExecPerspective *bpers;
+ bpers = QUERY_EXEC_PERSPECTIVE (perspective);
+ return bpers->priv->bwin;
+}
diff --git a/tools/browser/schema-browser/Makefile.am b/tools/browser/schema-browser/Makefile.am
index 49ffe03..d90749b 100644
--- a/tools/browser/schema-browser/Makefile.am
+++ b/tools/browser/schema-browser/Makefile.am
@@ -1,10 +1,16 @@
noinst_LTLIBRARIES = libperspective.la
+if LDAP
+ldap_flags=-DHAVE_LDAP
+endif
+
AM_CPPFLAGS = \
-I$(top_srcdir)/tools/browser \
-I$(top_builddir) \
-I$(top_srcdir) \
-I$(top_srcdir)/libgda \
+ -I$(top_srcdir)/libgda/sqlite \
+ $(ldap_flags) \
$(COREDEPS_CFLAGS) \
$(COREDEPS_WFLAGS) \
$(GTK_CFLAGS) \
diff --git a/tools/browser/schema-browser/schema-browser-perspective.c b/tools/browser/schema-browser/schema-browser-perspective.c
index c020af8..863094a 100644
--- a/tools/browser/schema-browser/schema-browser-perspective.c
+++ b/tools/browser/schema-browser/schema-browser-perspective.c
@@ -39,6 +39,7 @@ static void schema_browser_perspective_dispose (GObject *object);
/* BrowserPerspective interface */
static void schema_browser_perspective_perspective_init (BrowserPerspectiveIface *iface);
+static BrowserWindow *schema_browser_perspective_get_window (BrowserPerspective *perspective);
static GtkActionGroup *schema_browser_perspective_get_actions_group (BrowserPerspective *perspective);
static const gchar *schema_browser_perspective_get_actions_ui (BrowserPerspective *perspective);
static void schema_browser_perspective_page_tab_label_change (BrowserPerspective *perspective, BrowserPage *page);
@@ -104,6 +105,7 @@ schema_browser_perspective_class_init (SchemaBrowserPerspectiveClass * klass)
static void
schema_browser_perspective_perspective_init (BrowserPerspectiveIface *iface)
{
+ iface->i_get_window = schema_browser_perspective_get_window;
iface->i_get_actions_group = schema_browser_perspective_get_actions_group;
iface->i_get_actions_ui = schema_browser_perspective_get_actions_ui;
iface->i_page_tab_label_change = schema_browser_perspective_page_tab_label_change;
@@ -122,8 +124,6 @@ static void fav_selection_changed_cb (GtkWidget *widget, gint fav_id, BrowserFav
const gchar *selection, SchemaBrowserPerspective *bpers);
static void objects_index_selection_changed_cb (GtkWidget *widget, BrowserFavoritesType fav_type,
const gchar *selection, SchemaBrowserPerspective *bpers);
-static void nb_switch_page_cb (GtkNotebook *nb, GtkWidget *page, gint page_num,
- SchemaBrowserPerspective *perspective);
/**
* schema_browser_perspective_new
*
@@ -135,30 +135,31 @@ schema_browser_perspective_new (BrowserWindow *bwin)
BrowserConnection *bcnc;
BrowserPerspective *bpers;
SchemaBrowserPerspective *perspective;
+ gboolean fav_supported;
bpers = (BrowserPerspective*) g_object_new (TYPE_SCHEMA_BROWSER_PERSPECTIVE, NULL);
perspective = (SchemaBrowserPerspective*) bpers;
-
+ bcnc = browser_window_get_connection (bwin);
+ fav_supported = browser_connection_get_favorites (bcnc) ? TRUE : FALSE;
perspective->priv->bwin = bwin;
/* contents */
GtkWidget *paned, *wid, *nb;
- bcnc = browser_window_get_connection (bwin);
paned = gtk_hpaned_new ();
- wid = favorite_selector_new (bcnc);
- g_signal_connect (wid, "selection-changed",
- G_CALLBACK (fav_selection_changed_cb), bpers);
- gtk_paned_add1 (GTK_PANED (paned), wid);
- gtk_paned_set_position (GTK_PANED (paned), DEFAULT_FAVORITES_SIZE);
- perspective->priv->favorites = wid;
+ if (fav_supported) {
+ wid = favorite_selector_new (bcnc);
+ g_signal_connect (wid, "selection-changed",
+ G_CALLBACK (fav_selection_changed_cb), bpers);
+ gtk_paned_add1 (GTK_PANED (paned), wid);
+ gtk_paned_set_position (GTK_PANED (paned), DEFAULT_FAVORITES_SIZE);
+ perspective->priv->favorites = wid;
+ }
nb = gtk_notebook_new ();
perspective->priv->notebook = nb;
gtk_paned_add2 (GTK_PANED (paned), nb);
gtk_notebook_set_scrollable (GTK_NOTEBOOK (nb), TRUE);
gtk_notebook_popup_enable (GTK_NOTEBOOK (nb));
- g_signal_connect (G_OBJECT (nb), "switch-page",
- G_CALLBACK (nb_switch_page_cb), perspective);
wid = objects_index_new (bcnc);
g_signal_connect (wid, "selection-changed",
@@ -175,30 +176,12 @@ schema_browser_perspective_new (BrowserWindow *bwin)
gtk_box_pack_start (GTK_BOX (bpers), paned, TRUE, TRUE, 0);
gtk_widget_show_all (paned);
- if (!perspective->priv->favorites_shown)
+ if (perspective->priv->favorites && !perspective->priv->favorites_shown)
gtk_widget_hide (perspective->priv->favorites);
- return bpers;
-}
-
-static void
-nb_switch_page_cb (GtkNotebook *nb, G_GNUC_UNUSED GtkWidget *page, gint page_num,
- SchemaBrowserPerspective *perspective)
-{
- GtkWidget *page_contents;
- GtkActionGroup *actions = NULL;
- const gchar *ui = NULL;
+ browser_perspective_declare_notebook (bpers, GTK_NOTEBOOK (perspective->priv->notebook));
- page_contents = gtk_notebook_get_nth_page (nb, page_num);
- if (IS_BROWSER_PAGE (page_contents)) {
- actions = browser_page_get_actions_group (BROWSER_PAGE (page_contents));
- ui = browser_page_get_actions_ui (BROWSER_PAGE (page_contents));
- }
- browser_window_customize_perspective_ui (perspective->priv->bwin,
- BROWSER_PERSPECTIVE (perspective), actions,
- ui);
- if (actions)
- g_object_unref (actions);
+ return bpers;
}
static void
@@ -285,6 +268,7 @@ schema_browser_perspective_dispose (GObject *object)
perspective = SCHEMA_BROWSER_PERSPECTIVE (object);
if (perspective->priv) {
+ browser_perspective_declare_notebook ((BrowserPerspective*) perspective, NULL);
g_free (perspective->priv);
perspective->priv = NULL;
}
@@ -306,6 +290,9 @@ favorites_toggle_cb (GtkToggleAction *action, BrowserPerspective *bpers)
{
SchemaBrowserPerspective *perspective;
perspective = SCHEMA_BROWSER_PERSPECTIVE (bpers);
+ if (! perspective->priv->favorites)
+ return;
+
perspective->priv->favorites_shown = gtk_toggle_action_get_active (action);
if (perspective->priv->favorites_shown)
gtk_widget_show (perspective->priv->favorites);
@@ -343,19 +330,26 @@ static const gchar *ui_actions_info =
"</ui>";
static GtkActionGroup *
-schema_browser_perspective_get_actions_group (BrowserPerspective *bpers)
+schema_browser_perspective_get_actions_group (BrowserPerspective *perspective)
{
+ SchemaBrowserPerspective *bpers;
GtkActionGroup *agroup;
+ bpers = SCHEMA_BROWSER_PERSPECTIVE (perspective);
agroup = gtk_action_group_new ("SchemaBrowserActions");
gtk_action_group_set_translation_domain (agroup, GETTEXT_PACKAGE);
gtk_action_group_add_actions (agroup, ui_actions, G_N_ELEMENTS (ui_actions), bpers);
- gtk_action_group_add_toggle_actions (agroup, ui_toggle_actions, G_N_ELEMENTS (ui_toggle_actions),
+
+ gtk_action_group_add_toggle_actions (agroup, ui_toggle_actions,
+ G_N_ELEMENTS (ui_toggle_actions),
bpers);
GtkAction *action;
action = gtk_action_group_get_action (agroup, "SchemaBrowserFavoritesShow");
- gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
- SCHEMA_BROWSER_PERSPECTIVE (bpers)->priv->favorites_shown);
+ if (bpers->priv->favorites)
+ gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
+ bpers->priv->favorites_shown);
+ else
+ gtk_action_set_sensitive (GTK_ACTION (action), FALSE);
return agroup;
}
@@ -518,3 +512,11 @@ schema_browser_perspective_get_current_customization (BrowserPerspective *perspe
*out_ui = browser_page_get_actions_ui (BROWSER_PAGE (page_contents));
}
}
+
+static BrowserWindow *
+schema_browser_perspective_get_window (BrowserPerspective *perspective)
+{
+ SchemaBrowserPerspective *bpers;
+ bpers = SCHEMA_BROWSER_PERSPECTIVE (perspective);
+ return bpers->priv->bwin;
+}
diff --git a/tools/browser/schema-browser/table-columns.c b/tools/browser/schema-browser/table-columns.c
index a8cc96e..aa82b00 100644
--- a/tools/browser/schema-browser/table-columns.c
+++ b/tools/browser/schema-browser/table-columns.c
@@ -35,6 +35,9 @@
#include "schema-browser-perspective.h"
#include "../browser-window.h"
#include "../common/fk-declare.h"
+#ifdef HAVE_LDAP
+#include "../ldap-browser/ldap-browser-perspective.h"
+#endif
struct _TableColumnsPrivate {
BrowserConnection *bcnc;
@@ -44,11 +47,18 @@ struct _TableColumnsPrivate {
GtkTextBuffer *constraints;
gboolean hovering_over_link;
+#ifdef HAVE_LDAP
+ GtkTextBuffer *ldap_def;
+ GtkWidget *ldap_header;
+ GtkWidget *ldap_text;
+ gboolean ldap_props_shown;
+#endif
};
static void table_columns_class_init (TableColumnsClass *klass);
static void table_columns_init (TableColumns *tcolumns, TableColumnsClass *klass);
-static void table_columns_dispose (GObject *object);
+static void table_columns_dispose (GObject *object);
+static void table_columns_show_all (GtkWidget *widget);
static void meta_changed_cb (BrowserConnection *bcnc, GdaMetaStruct *mstruct, TableColumns *tcolumns);
@@ -67,6 +77,7 @@ table_columns_class_init (TableColumnsClass *klass)
parent_class = g_type_class_peek_parent (klass);
object_class->dispose = table_columns_dispose;
+ GTK_WIDGET_CLASS (klass)->show_all = table_columns_show_all;
}
@@ -101,6 +112,19 @@ table_columns_dispose (GObject *object)
parent_class->dispose (object);
}
+static void
+table_columns_show_all (GtkWidget *widget)
+{
+ TableColumns *tcolumns = (TableColumns *) widget;
+ GTK_WIDGET_CLASS (parent_class)->show_all (widget);
+ if (browser_connection_is_ldap (tcolumns->priv->bcnc)) {
+ if (! tcolumns->priv->ldap_props_shown) {
+ gtk_widget_hide (tcolumns->priv->ldap_header);
+ gtk_widget_hide (tcolumns->priv->ldap_text);
+ }
+ }
+}
+
GType
table_columns_get_type (void)
{
@@ -157,7 +181,15 @@ meta_changed_cb (G_GNUC_UNUSED BrowserConnection *bcnc, GdaMetaStruct *mstruct,
GtkTextBuffer *tbuffer;
GtkTextIter start, end;
- /* constraints descr. cleaning */
+ /* cleanups */
+#ifdef HAVE_LDAP
+ if (browser_connection_is_ldap (tcolumns->priv->bcnc)) {
+ tbuffer = tcolumns->priv->ldap_def;
+ gtk_text_buffer_get_start_iter (tbuffer, &start);
+ gtk_text_buffer_get_end_iter (tbuffer, &end);
+ gtk_text_buffer_delete (tbuffer, &start, &end);
+ }
+#endif
tbuffer = tcolumns->priv->constraints;
gtk_text_buffer_get_start_iter (tbuffer, &start);
gtk_text_buffer_get_end_iter (tbuffer, &end);
@@ -420,6 +452,81 @@ meta_changed_cb (G_GNUC_UNUSED BrowserConnection *bcnc, GdaMetaStruct *mstruct,
g_slist_free (rev_list);
gtk_text_buffer_insert (tbuffer, ¤t, "\n", -1);
}
+
+#ifdef HAVE_LDAP
+ if (browser_connection_is_ldap (tcolumns->priv->bcnc)) {
+ const gchar *base_dn, *filter, *attributes, *scope_str;
+ GdaLdapSearchScope scope;
+ tbuffer = tcolumns->priv->ldap_def;
+ gtk_text_buffer_get_start_iter (tbuffer, ¤t);
+ if (browser_connection_describe_table (tcolumns->priv->bcnc, dbo->obj_name,
+ &base_dn, &filter,
+ &attributes, &scope, NULL)) {
+ gtk_text_buffer_insert_with_tags_by_name (tbuffer, ¤t,
+ "BASE: ", -1,
+ "section", NULL);
+ if (base_dn) {
+ GtkTextTag *tag;
+ tag = gtk_text_buffer_create_tag (tbuffer, NULL,
+ "foreground", "blue",
+ "weight", PANGO_WEIGHT_NORMAL,
+ "underline", PANGO_UNDERLINE_SINGLE,
+ NULL);
+ g_object_set_data_full (G_OBJECT (tag), "dn",
+ g_strdup (base_dn), g_free);
+
+ gtk_text_buffer_insert_with_tags (tbuffer, ¤t, base_dn, -1,
+ tag, NULL);
+ }
+
+ gtk_text_buffer_insert (tbuffer, ¤t, "\n", -1);
+ gtk_text_buffer_insert_with_tags_by_name (tbuffer, ¤t,
+ "FILTER: ", -1,
+ "section", NULL);
+ if (filter)
+ gtk_text_buffer_insert (tbuffer, ¤t, filter, -1);
+
+ gtk_text_buffer_insert (tbuffer, ¤t, "\n", -1);
+ gtk_text_buffer_insert_with_tags_by_name (tbuffer, ¤t,
+ "ATTRIBUTES: ", -1,
+ "section", NULL);
+ if (attributes)
+ gtk_text_buffer_insert (tbuffer, ¤t, attributes, -1);
+
+ gtk_text_buffer_insert (tbuffer, ¤t, "\n", -1);
+ gtk_text_buffer_insert_with_tags_by_name (tbuffer, ¤t,
+ "SCOPE: ", -1,
+ "section", NULL);
+
+ switch (scope) {
+ case GDA_LDAP_SEARCH_BASE:
+ scope_str = "base";
+ break;
+ case GDA_LDAP_SEARCH_ONELEVEL:
+ scope_str = "onelevel";
+ break;
+ case GDA_LDAP_SEARCH_SUBTREE:
+ scope_str = "subtree";
+ break;
+ default:
+ TO_IMPLEMENT;
+ scope_str = _("Unknown");
+ break;
+ }
+ gtk_text_buffer_insert (tbuffer, ¤t, scope_str, -1);
+
+ tcolumns->priv->ldap_props_shown = TRUE;
+ gtk_widget_show (tcolumns->priv->ldap_header);
+ gtk_widget_show (tcolumns->priv->ldap_text);
+ }
+ else {
+ tcolumns->priv->ldap_props_shown = FALSE;
+ gtk_widget_hide (tcolumns->priv->ldap_header);
+ gtk_widget_hide (tcolumns->priv->ldap_text);
+ }
+ }
+#endif
+
}
if (schema_v)
@@ -566,15 +673,63 @@ table_columns_new (TableInfo *tinfo)
gtk_container_add (GTK_CONTAINER (sw), treeview);
gtk_paned_pack1 (GTK_PANED (paned), sw, TRUE, FALSE);
+ /* Paned, part 2 */
+ GtkWidget *vbox;
+ vbox = gtk_vbox_new (FALSE, 0);
+ gtk_paned_pack2 (GTK_PANED (paned), vbox, TRUE, TRUE);
+
+#ifdef HAVE_LDAP
+ if (browser_connection_is_ldap (tcolumns->priv->bcnc)) {
+ GtkWidget *label;
+ gchar *str;
+
+ str = g_strdup_printf ("<b>%s</b>", _("LDAP virtual table definition"));
+ label = cc_gray_bar_new (str);
+ g_free (str);
+ gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+ tcolumns->priv->ldap_header = label;
+
+ sw = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw), GTK_SHADOW_NONE);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
+ GTK_POLICY_NEVER,
+ GTK_POLICY_AUTOMATIC);
+ gtk_box_pack_start (GTK_BOX (vbox), sw, TRUE, TRUE, 0);
+ tcolumns->priv->ldap_text = sw;
+
+ GtkWidget *textview;
+ textview = gtk_text_view_new ();
+ gtk_container_add (GTK_CONTAINER (sw), textview);
+ gtk_text_view_set_left_margin (GTK_TEXT_VIEW (textview), 5);
+ gtk_text_view_set_right_margin (GTK_TEXT_VIEW (textview), 5);
+ gtk_text_view_set_editable (GTK_TEXT_VIEW (textview), FALSE);
+ tcolumns->priv->ldap_def = gtk_text_view_get_buffer (GTK_TEXT_VIEW (textview));
+ gtk_text_buffer_set_text (tcolumns->priv->ldap_def, "aa", -1);
+
+ gtk_text_buffer_create_tag (tcolumns->priv->ldap_def, "section",
+ "weight", PANGO_WEIGHT_BOLD,
+ "foreground", "blue", NULL);
+
+ gtk_text_buffer_create_tag (tcolumns->priv->ldap_def, "warning",
+ "foreground", "red", NULL);
+
+ g_signal_connect (textview, "key-press-event",
+ G_CALLBACK (key_press_event), tcolumns);
+ g_signal_connect (textview, "event-after",
+ G_CALLBACK (event_after), tcolumns);
+ g_signal_connect (textview, "motion-notify-event",
+ G_CALLBACK (motion_notify_event), tcolumns);
+ g_signal_connect (textview, "visibility-notify-event",
+ G_CALLBACK (visibility_notify_event), tcolumns);
+ }
+#endif
+
/*
* Constraints
*/
- GtkWidget *vbox, *label;
+ GtkWidget *label;
gchar *str;
- vbox = gtk_vbox_new (FALSE, 0);
- gtk_paned_pack2 (GTK_PANED (paned), vbox, TRUE, TRUE);
-
str = g_strdup_printf ("<b>%s</b>", _("Constraints and integrity rules"));
label = cc_gray_bar_new (str);
g_free (str);
@@ -613,6 +768,8 @@ table_columns_new (TableInfo *tinfo)
g_signal_connect (textview, "visibility-notify-event",
G_CALLBACK (visibility_notify_event), tcolumns);
+ gtk_widget_show_all (vbox);
+
/*
* initial update
*/
@@ -645,7 +802,8 @@ set_cursor_if_appropriate (GtkTextView *text_view, gint x, gint y, TableColumns
GtkTextTag *tag = tagp->data;
if (g_object_get_data (G_OBJECT (tag), "table_name") ||
- g_object_get_data (G_OBJECT (tag), "fk_name")) {
+ g_object_get_data (G_OBJECT (tag), "fk_name") ||
+ g_object_get_data (G_OBJECT (tag), "dn")) {
hovering = TRUE;
break;
}
@@ -730,15 +888,17 @@ follow_if_link (G_GNUC_UNUSED GtkWidget *text_view, GtkTextIter *iter, TableColu
const gchar *table_schema;
const gchar *table_short_name;
const gchar *fk_name;
+ const gchar *dn;
SchemaBrowserPerspective *bpers;
table_schema = g_object_get_data (G_OBJECT (tag), "table_schema");
table_name = g_object_get_data (G_OBJECT (tag), "table_name");
table_short_name = g_object_get_data (G_OBJECT (tag), "table_short_name");
fk_name = g_object_get_data (G_OBJECT (tag), "fk_name");
+ dn = g_object_get_data (G_OBJECT (tag), "dn");
bpers = SCHEMA_BROWSER_PERSPECTIVE (browser_find_parent_widget (GTK_WIDGET (tcolumns),
- TYPE_SCHEMA_BROWSER_PERSPECTIVE));
+ TYPE_SCHEMA_BROWSER_PERSPECTIVE));
if (table_name && table_schema && table_short_name && bpers) {
schema_browser_perspective_display_table_info (bpers,
table_schema,
@@ -800,6 +960,17 @@ follow_if_link (G_GNUC_UNUSED GtkWidget *text_view, GtkTextIter *iter, TableColu
fk_name);
}
}
+#ifdef HAVE_LDAP
+ else if (dn) {
+ BrowserWindow *bwin;
+ BrowserPerspective *pers;
+
+ bwin = (BrowserWindow*) gtk_widget_get_toplevel ((GtkWidget*) tcolumns);
+ pers = browser_window_change_perspective (bwin, _("LDAP browser"));
+
+ ldap_browser_perspective_display_ldap_entry (LDAP_BROWSER_PERSPECTIVE (pers), dn);
+ }
+#endif
}
if (tags)
diff --git a/tools/browser/support.c b/tools/browser/support.c
index e52a6bc..5b1b4e6 100644
--- a/tools/browser/support.c
+++ b/tools/browser/support.c
@@ -371,7 +371,7 @@ GdkPixbuf *
browser_get_pixbuf_icon (BrowserIconType type)
{
static GdkPixbuf **array = NULL;
- static const gchar* names[] = {
+ static const gchar* names[] = { /* array indexed by BrowserIconType */
"gda-browser-bookmark.png",
"gda-browser-schema.png",
"gda-browser-table.png",
@@ -387,6 +387,14 @@ browser_get_pixbuf_icon (BrowserIconType type)
"gda-browser-grid.png",
"gda-browser-form.png",
"gda-browser-menu-ind.png",
+ "gda-browser-ldap-entry.png",
+ "gda-browser-ldap-group.png",
+ "gda-browser-ldap-organization.png",
+ "gda-browser-ldap-person.png",
+ "gda-browser-ldap-class-s.png",
+ "gda-browser-ldap-class-a.png",
+ "gda-browser-ldap-class-x.png",
+ "gda-browser-ldap-class-u.png",
};
if (!array)
@@ -406,6 +414,45 @@ browser_get_pixbuf_icon (BrowserIconType type)
return array [type];
}
+#ifdef HAVE_LDAP
+/**
+ * browser_get_pixbuf_for_ldap_class:
+ */
+GdkPixbuf *
+browser_get_pixbuf_for_ldap_class (GdaLdapClassKind kind)
+{
+ switch (kind) {
+ case GDA_LDAP_CLASS_KIND_ABSTRACT:
+ return browser_get_pixbuf_icon (BROWSER_ICON_LDAP_CLASS_ABSTRACT);
+ case GDA_LDAP_CLASS_KIND_STRUTURAL:
+ return browser_get_pixbuf_icon (BROWSER_ICON_LDAP_CLASS_STRUCTURAL);
+ case GDA_LDAP_CLASS_KIND_AUXILIARY:
+ return browser_get_pixbuf_icon (BROWSER_ICON_LDAP_CLASS_AUXILIARY);
+ default:
+ return browser_get_pixbuf_icon (BROWSER_ICON_LDAP_CLASS_UNKNOWN);
+ }
+}
+
+/**
+ * browser_get_kind_for_ldap_class:
+ */
+const gchar *
+browser_get_kind_for_ldap_class (GdaLdapClassKind kind)
+{
+ switch (kind) {
+ case GDA_LDAP_CLASS_KIND_ABSTRACT:
+ return _("Abstract");
+ case GDA_LDAP_CLASS_KIND_STRUTURAL:
+ return _("Structural");
+ case GDA_LDAP_CLASS_KIND_AUXILIARY:
+ return _("Auxilliary");
+ default:
+ return _("Unknown");
+ }
+}
+#endif
+
+
/**
* browser_find_parent_widget
*
diff --git a/tools/browser/support.h b/tools/browser/support.h
index d1af082..d9b8b2a 100644
--- a/tools/browser/support.h
+++ b/tools/browser/support.h
@@ -30,6 +30,9 @@
#include <gtkosxapplication.h>
extern GtkOSXApplication *theApp;
#endif
+#ifdef HAVE_LDAP
+#include <libgda/sqlite/virtual/gda-ldap-connection.h>
+#endif
G_BEGIN_DECLS
@@ -67,7 +70,7 @@ GtkWidget *browser_make_tree_view (GtkTreeModel *model);
GtkWidget *browser_find_parent_widget (GtkWidget *current, GType requested_type);
/*
- * icons
+ * icons, see browser_get_pixbuf_icon() for the associated icons
*/
typedef enum {
BROWSER_ICON_BOOKMARK,
@@ -88,10 +91,23 @@ typedef enum {
BROWSER_ICON_MENU_INDICATOR,
+ BROWSER_ICON_LDAP_ENTRY,
+ BROWSER_ICON_LDAP_GROUP,
+ BROWSER_ICON_LDAP_ORGANIZATION,
+ BROWSER_ICON_LDAP_PERSON,
+ BROWSER_ICON_LDAP_CLASS_STRUCTURAL,
+ BROWSER_ICON_LDAP_CLASS_ABSTRACT,
+ BROWSER_ICON_LDAP_CLASS_AUXILIARY,
+ BROWSER_ICON_LDAP_CLASS_UNKNOWN,
+
BROWSER_ICON_LAST
} BrowserIconType;
GdkPixbuf *browser_get_pixbuf_icon (BrowserIconType type);
+#ifdef HAVE_LDAP
+GdkPixbuf *browser_get_pixbuf_for_ldap_class (GdaLdapClassKind kind);
+const gchar *browser_get_kind_for_ldap_class (GdaLdapClassKind kind);
+#endif
/*
* Connections list
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]