Export address book to HTML.
authorMatch Grun <match@dimensional.com>
Mon, 6 May 2002 06:27:11 +0000 (06:27 +0000)
committerMatch Grun <match@dimensional.com>
Mon, 6 May 2002 06:27:11 +0000 (06:27 +0000)
14 files changed:
ChangeLog.claws
configure.in
src/Makefile.am
src/addrcache.c
src/addressbook.c
src/addrharvest.c
src/addritem.c
src/addritem.h
src/exphtmldlg.c [new file with mode: 0644]
src/exphtmldlg.h [new file with mode: 0644]
src/exporthtml.c [new file with mode: 0644]
src/exporthtml.h [new file with mode: 0644]
src/xmlprops.c [new file with mode: 0644]
src/xmlprops.h [new file with mode: 0644]

index fe53a60e5d0ed431f529d90d36e1a5b726fbce4c..0ee353377750989066c35f689e7cfbfe280c4084 100644 (file)
@@ -1,3 +1,16 @@
+2002-05-06 [match]     0.7.5claws9
+
+       * src/addritem.[ch]
+               new functions.
+       * src/addrcache.c
+               tidy up.
+       * src/xmlprops.[ch]
+               XML implementation of properties file.
+       * src/exporthtml.[ch]
+       * src/expthtmldlg.[ch]
+       * src/addressbook.c
+               export to HTML.
+
 2002-05-02 [sergey]    0.7.5claws8
 
        * src/mainwindow.c (mainwin_entries),
index 7792c4f6e51d7194d72632131012b099ece85519..7af1ee9f38a50169d18beb155ccdadbfa7c9d9ae 100644 (file)
@@ -8,7 +8,7 @@ MINOR_VERSION=7
 MICRO_VERSION=5
 INTERFACE_AGE=0
 BINARY_AGE=0
-EXTRA_VERSION=claws8
+EXTRA_VERSION=claws9
 VERSION=$MAJOR_VERSION.$MINOR_VERSION.$MICRO_VERSION$EXTRA_VERSION
 
 dnl set $target
index 8d25ca3bfdcb1735c86e36d144dd88f9295d2169..ad9a4481e8413150a8a88c4722ef9bf3d2a30270 100644 (file)
@@ -69,6 +69,9 @@ sylpheed_SOURCES = \
        addressadd.c addressadd.h \
        addrharvest.c addrharvest.h \
        addrgather.c addrgather.h \
+       exporthtml.c exporthtml.h \
+       exphtmldlg.c exphtmldlg.h \
+       xmlprops.c xmlprops.h \
        filesel.c filesel.h \
        foldersel.c foldersel.h \
        statusbar.c statusbar.h \
index bd60b08e0b72590692eda9cf9e48d81497a7ab51..ccf386539a5ab7ad9154f2b5107213968f9d87da 100644 (file)
@@ -172,7 +172,7 @@ static gint addrcache_free_item_vis( gpointer key, gpointer value, gpointer data
        }
        key = NULL;
        value = NULL;
-       return 0;
+       return TRUE;
 }
 
 /*
@@ -183,17 +183,21 @@ static void addrcache_free_item_hash( GHashTable *table ) {
        g_hash_table_freeze( table );
        g_hash_table_foreach_remove( table, addrcache_free_item_vis, NULL );
        g_hash_table_thaw( table );
-       g_hash_table_destroy( table );
 }
 
 /*
 * Free up folders and groups.
 */
 static void addrcache_free_all_folders( ItemFolder *parent ) {
-       GList *node = parent->listFolder;
+       GList *node;
+
+       if( parent == NULL ) return;
+
+       node = parent->listFolder;
        while( node ) {
                ItemFolder *folder = node->data;
                addrcache_free_all_folders( folder );
+               node->data = NULL;
                node = g_list_next( node );
        }
        g_list_free( parent->listPerson );
@@ -214,11 +218,12 @@ void addrcache_clear( AddressCache *cache ) {
        /* Free up folders and hash table */
        addrcache_free_all_folders( cache->rootFolder );
        addrcache_free_item_hash( cache->itemHash );
+       g_hash_table_destroy( cache->itemHash );
        cache->itemHash = NULL;
        ADDRITEM_PARENT(cache->rootFolder) = NULL;
        addritem_free_item_folder( cache->rootFolder );
        cache->rootFolder = NULL;
-       g_list_free( cache->tempList );
+       if( cache->tempList ) g_list_free( cache->tempList );
        cache->tempList = NULL;
 
        /* Reset to initial state */
@@ -228,7 +233,6 @@ void addrcache_clear( AddressCache *cache ) {
        ADDRITEM_PARENT(cache->rootFolder) = NULL;
 
        addrcache_refresh( cache );
-
 }
 
 /*
@@ -240,6 +244,7 @@ void addrcache_free( AddressCache *cache ) {
        cache->dirtyFlag = FALSE;
        addrcache_free_all_folders( cache->rootFolder );
        addrcache_free_item_hash( cache->itemHash );
+       g_hash_table_destroy( cache->itemHash );
        cache->itemHash = NULL;
        ADDRITEM_PARENT(cache->rootFolder) = NULL;
        addritem_free_item_folder( cache->rootFolder );
index 00323e0c0d68956890ddf4a3f3d58ac723e1fbdd..3beba73287bc0db6fbd069021f8a5e619b8b499d 100644 (file)
 #include "editgroup.h"
 #include "editaddress.h"
 #include "editbook.h"
-#include "ldif.h"
 #include "importldif.h"
-#include "mutt.h"
 #include "importmutt.h"
-#include "pine.h"
 #include "importpine.h"
 
 #ifdef USE_JPILOT
@@ -97,6 +94,8 @@
 #include "addrselect.h"
 #include "addrclip.h"
 #include "addrgather.h"
+#include "adbookbase.h"
+#include "exphtmldlg.h"
 
 typedef enum
 {
@@ -335,6 +334,7 @@ static void addressbook_list_select_remove  ( AddrItemObject    *aio );
 static void addressbook_import_ldif_cb         ( void );
 static void addressbook_import_mutt_cb         ( void );
 static void addressbook_import_pine_cb         ( void );
+static void addressbook_export_html_cb         ( void );
 static void addressbook_clip_cut_cb            ( void );
 static void addressbook_clip_copy_cb           ( void );
 static void addressbook_clip_paste_cb          ( void );
@@ -374,23 +374,15 @@ static GtkItemFactoryEntry addressbook_entries[] =
        {N_("/_Address/_Edit"),         "<alt>Return",  addressbook_edit_address_cb,    0, NULL},
        {N_("/_Address/_Delete"),       NULL,           addressbook_delete_address_cb,  0, NULL},
        {N_("/_Tools/---"),             NULL,           NULL, 0, "<Separator>"},
-       {N_("/_Tools/Import _LDIF file"), NULL,         addressbook_import_ldif_cb,     0, NULL},
-       {N_("/_Tools/Import M_utt file"), NULL,         addressbook_import_mutt_cb,     0, NULL},
-       {N_("/_Tools/Import _Pine file"), NULL,         addressbook_import_pine_cb,     0, NULL},
+       {N_("/_Tools/Import _LDIF file..."), NULL,      addressbook_import_ldif_cb,     0, NULL},
+       {N_("/_Tools/Import M_utt file..."), NULL,      addressbook_import_mutt_cb,     0, NULL},
+       {N_("/_Tools/Import _Pine file..."), NULL,      addressbook_import_pine_cb,     0, NULL},
+       {N_("/_Tools/---"),             NULL,           NULL, 0, "<Separator>"},
+       {N_("/_Tools/Export _HTML..."), NULL,           addressbook_export_html_cb,     0, NULL},
        {N_("/_Help"),                  NULL,           NULL, 0, "<LastBranch>"},
        {N_("/_Help/_About"),           NULL,           about_show, 0, NULL}
 };
 
-/* New options to be added. */
-/*
-       {N_("/_Tools"),                 NULL,           NULL, 0, "<Branch>"},
-       {N_("/_Tools/Import _Mozilla"), NULL,           NULL,                           0, NULL},
-       {N_("/_Tools/Import _vCard"),   NULL,           NULL,                           0, NULL},
-       {N_("/_Tools/---"),             NULL,           NULL, 0, "<Separator>"},
-       {N_("/_Tools/Export _LDIF file"), NULL,         NULL,                           0, NULL},
-       {N_("/_Tools/Export v_Card"),   NULL,           NULL,                           0, NULL},
-*/
-
 static GtkItemFactoryEntry addressbook_tree_popup_entries[] =
 {
        {N_("/New _Address"),   NULL, addressbook_new_address_cb, 0, NULL},
@@ -791,8 +783,7 @@ static void addressbook_create(void)
 
 }
 
-static gint addressbook_close(void)
-{
+static gint addressbook_close( void ) {
        gtk_widget_hide(addrbook.window);
        addressbook_export_to_file();
        return TRUE;
@@ -897,7 +888,7 @@ static void addressbook_del_clicked(GtkButton *button, gpointer data)
        /* Test for read only */
        iface = ds->interface;
        if( iface->readOnly ) {
-               aval = alertpanel( _("Delete address(es)"),
+               alertpanel( _("Delete address(es)"),
                        _("This address data is readonly and cannot be deleted."),
                        _("Close"), NULL, NULL );
                return;
@@ -1114,6 +1105,7 @@ static void addressbook_menuitem_set_sensitive( AddressObject *obj, GtkCTreeNode
        gboolean canAdd = FALSE;
        gboolean canEditTr = TRUE;
        gboolean editAddress = FALSE;
+       gboolean canExport = TRUE;
        AddressTypeControlItem *atci = NULL;
        AddressDataSource *ds = NULL;
        AddressInterface *iface = NULL;
@@ -1129,7 +1121,7 @@ static void addressbook_menuitem_set_sensitive( AddressObject *obj, GtkCTreeNode
                                menu_set_sensitive( addrbook.menu_factory, atci->menuCommand, TRUE );
                        }
                }
-               canEditTr = FALSE;
+               canEditTr = canExport = FALSE;
        }
        else if( obj->type == ADDR_DATASOURCE ) {
                AdapterDSource *ads = ADAPTER_DSOURCE(obj);
@@ -1139,7 +1131,7 @@ static void addressbook_menuitem_set_sensitive( AddressObject *obj, GtkCTreeNode
                        canAdd = canEdit = editAddress = TRUE;
                }
                if( ! iface->haveLibrary ) {
-                       canAdd = canEdit = editAddress = FALSE;
+                       canAdd = canEdit = editAddress = canExport = FALSE;
                }
        }
        else if( obj->type == ADDR_ITEM_FOLDER ) {
@@ -1179,6 +1171,9 @@ static void addressbook_menuitem_set_sensitive( AddressObject *obj, GtkCTreeNode
 
        menu_set_sensitive( addrbook.menu_factory, "/File/Edit",      canEditTr );
        menu_set_sensitive( addrbook.menu_factory, "/File/Delete",    canEditTr );
+
+       /* Export data */
+       menu_set_sensitive( addrbook.menu_factory, "/Tools/Export HTML...", canExport );
 }
 
 static void addressbook_tree_selected(GtkCTree *ctree, GtkCTreeNode *node,
@@ -4003,6 +3998,30 @@ void addressbook_harvest(
        }
 }
 
+/*
+* Export HTML file.
+*/
+static void addressbook_export_html_cb( void ) {
+       GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
+       AddressObject *obj;
+       AddressDataSource *ds = NULL;
+       AddrBookBase *adbase;
+       AddressCache *cache;
+       GtkCTreeNode *node = NULL;
+
+       if( ! addrbook.treeSelected ) return;
+       node = addrbook.treeSelected;
+       if( GTK_CTREE_ROW(node)->level == 1 ) return;
+       obj = gtk_ctree_node_get_row_data( ctree, node );
+       if( obj == NULL ) return;
+
+       ds = addressbook_find_datasource( node );
+       if( ds == NULL ) return;
+       adbase = ( AddrBookBase * ) ds->rawDataSource;
+       cache = adbase->addressCache;
+       addressbook_exp_html( cache );
+}
+
 /*
 * End of Source.
 */
index 4a7516332626298f4dd303a4c35a9737ebdf9b85..b3a3f021d0f5e9ef4c89b9a01eb48763cc039c6e 100644 (file)
@@ -19,7 +19,6 @@
 
 /*
  * Functions for an E-Mail address harvester.
- * Code still needs some work. Address parsing not strictly correct.
  */
 
 #include <sys/stat.h>
index 84e92e7bfff0ed4b2bd7df059e73984fd19ff0b4..2a440e05a16733eee339662b3945b960d390f7de 100644 (file)
@@ -1125,6 +1125,33 @@ void addritem_parse_first_last( ItemPerson *person ) {
                g_strstrip( person->lastName );
 }
 
+/*
+ * Build a path of all ancestor folders for specified folder.
+ * Enter:  folder Folder.
+ *         seq    Path sequence, FALSE top down, TRUE bottom up.
+ * Return: List of folders from the top down.
+ */
+GList *addritem_folder_path( const ItemFolder *folder, const gboolean seq ) {
+       GList *list;
+       AddrItemObject *item;
+
+       list = NULL;
+       item = ( AddrItemObject * ) folder;
+       if( seq ) {
+               while( item ) {
+                       list = g_list_prepend( list, item );
+                       item = ADDRITEM_PARENT( item );
+               }
+       }
+       else {
+               while( item ) {
+                       list = g_list_append( list, item );
+                       item = ADDRITEM_PARENT( item );
+               }
+       }
+       return list;
+}
+
 /*
 * End of Source.
 */
index 5b494e8e3106c4ad60e99b302077f28f64f7dc88..910f7d71615425cada1ccacfc6d8cde60660d305 100644 (file)
@@ -160,9 +160,9 @@ void addritem_folder_set_remarks    ( ItemFolder *folder, const gchar *value );
 void addritem_free_item_folder         ( ItemFolder *folder );
 void addritem_free_item_folder_recurse ( ItemFolder *parent );
 
-gboolean addritem_group_add_email              ( ItemGroup *group, ItemEMail *email );
-ItemEMail *addritem_group_remove_email         ( ItemGroup *group, ItemEMail *email );
-ItemEMail *addritem_group_remove_email_id      ( ItemGroup *group, const gchar *eid );
+gboolean addritem_group_add_email      ( ItemGroup *group, ItemEMail *email );
+ItemEMail *addritem_group_remove_email ( ItemGroup *group, ItemEMail *email );
+ItemEMail *addritem_group_remove_email_id( ItemGroup *group, const gchar *eid );
 
 gboolean addritem_folder_add_person    ( ItemFolder *folder, ItemPerson *item );
 gboolean addritem_folder_add_folder    ( ItemFolder *folder, ItemFolder *item );
@@ -171,9 +171,15 @@ void addritem_folder_free_person   ( ItemFolder *folder );
 GList *addritem_folder_get_person_list ( ItemFolder *folder );
 GList *addritem_folder_get_group_list  ( ItemFolder *folder );
 
-ItemEMail *addritem_move_email_before  ( ItemPerson *person, ItemEMail *itemMove, ItemEMail *itemTarget );
-ItemEMail *addritem_move_email_after   ( ItemPerson *person, ItemEMail *itemMove, ItemEMail *itemTarget );
+ItemEMail *addritem_move_email_before  ( ItemPerson *person,
+                                         ItemEMail *itemMove,
+                                         ItemEMail *itemTarget );
+ItemEMail *addritem_move_email_after   ( ItemPerson *person,
+                                         ItemEMail *itemMove,
+                                         ItemEMail *itemTarget );
 
 void addritem_parse_first_last         ( ItemPerson *person );
+GList *addritem_folder_path            ( const ItemFolder *folder,
+                                         const gboolean seq );
 
 #endif /* __ADDRITEM_H__ */
diff --git a/src/exphtmldlg.c b/src/exphtmldlg.c
new file mode 100644 (file)
index 0000000..9b47d8f
--- /dev/null
@@ -0,0 +1,760 @@
+/*
+ * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 2002 Match Grun
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * Export address book to HTML file.
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include "defs.h"
+
+#include <glib.h>
+#include <gdk/gdkkeysyms.h>
+#include <gtk/gtkwindow.h>
+#include <gtk/gtksignal.h>
+#include <gtk/gtklabel.h>
+#include <gtk/gtkentry.h>
+#include <gtk/gtktable.h>
+#include <gtk/gtkbutton.h>
+
+#include "intl.h"
+#include "gtkutils.h"
+#include "prefs_common.h"
+#include "alertpanel.h"
+#include "mgutils.h"
+#include "addrcache.h"
+#include "addressitem.h"
+#include "exporthtml.h"
+
+#define PAGE_FILE_INFO             0
+#define PAGE_FORMAT                1
+#define PAGE_FINISH                2
+
+#define EXPORTHTML_WIDTH           480
+#define EXPORTHTML_HEIGHT          -1
+
+static struct _ExpHtml_Dlg {
+       GtkWidget *window;
+       GtkWidget *notebook;
+       GtkWidget *labelBook;
+       GtkWidget *entryHtml;
+       GtkWidget *optmenuCSS;
+       GtkWidget *optmenuName;
+       GtkWidget *checkBanding;
+       GtkWidget *checkLinkEMail;
+       GtkWidget *checkAttributes;
+       GtkWidget *labelOutBook;
+       GtkWidget *labelOutFile;
+       GtkWidget *btnPrev;
+       GtkWidget *btnNext;
+       GtkWidget *btnCancel;
+       GtkWidget *statusbar;
+       gint      status_cid;
+       gboolean  cancelled;
+} exphtml_dlg;
+
+static struct _AddressFileSelection _exp_html_file_selector_;
+
+static ExportHtmlCtl *_exportCtl_ = NULL;
+static AddressCache *_addressCache_ = NULL;
+
+static void export_html_status_show( gchar *msg ) {
+       if( exphtml_dlg.statusbar != NULL ) {
+               gtk_statusbar_pop(
+                       GTK_STATUSBAR(exphtml_dlg.statusbar),
+                       exphtml_dlg.status_cid );
+               if( msg ) {
+                       gtk_statusbar_push(
+                               GTK_STATUSBAR(exphtml_dlg.statusbar),
+                               exphtml_dlg.status_cid, msg );
+               }
+       }
+}
+
+static void export_html_message( void ) {
+       gchar *sMsg = NULL;
+       gint pageNum;
+
+       pageNum = gtk_notebook_current_page( GTK_NOTEBOOK(exphtml_dlg.notebook) );
+       if( pageNum == PAGE_FILE_INFO ) {
+               sMsg = _( "Please specify output directory and file to create." );
+       }
+       else if( pageNum == PAGE_FORMAT ) {
+               sMsg = _( "Select stylesheet and formatting." );
+       }
+       else if( pageNum == PAGE_FINISH ) {
+               sMsg = _( "File exported successfully." );
+       }
+       export_html_status_show( sMsg );
+}
+
+static void export_html_cancel( GtkWidget *widget, gpointer data ) {
+       gint pageNum;
+
+       pageNum = gtk_notebook_current_page( GTK_NOTEBOOK(exphtml_dlg.notebook) );
+       if( pageNum != PAGE_FINISH ) {
+               exphtml_dlg.cancelled = TRUE;
+       }
+       gtk_main_quit();
+}
+
+static gint export_html_delete_event( GtkWidget *widget, GdkEventAny *event, gpointer data ) {
+       export_html_cancel( widget, data );
+       return TRUE;
+}
+
+static void export_html_key_pressed( GtkWidget *widget, GdkEventKey *event, gpointer data ) {
+       if (event && event->keyval == GDK_Escape) {
+               export_html_cancel( widget, data );
+       }
+}
+
+/*
+ * Move off file info page.
+ * return: TRUE if OK to move off page.
+ */
+static gboolean exp_html_move_file() {
+       gchar *sFile, *msg, *reason;
+       AlertValue aval;
+
+       sFile = gtk_editable_get_chars( GTK_EDITABLE(exphtml_dlg.entryHtml), 0, -1 );
+       g_strchug( sFile ); g_strchomp( sFile );
+       gtk_entry_set_text( GTK_ENTRY(exphtml_dlg.entryHtml), sFile );
+       exporthtml_parse_filespec( _exportCtl_, sFile );
+       g_free( sFile );
+
+       /* Test for directory */
+       if( exporthtml_test_dir( _exportCtl_ ) ) {
+               return TRUE;
+       }
+
+       /* Prompt to create */
+       msg = g_strdup_printf( _(
+               "HTML Output Directory '%s'\n" \
+               "does not exist. OK to create new directory?" ),
+               _exportCtl_->dirOutput );
+       aval = alertpanel( _("Create Directory" ),
+               msg, _( "Yes" ), _( "No" ), NULL );
+       g_free( msg );
+       if( aval != G_ALERTDEFAULT ) return FALSE;
+
+       /* Create directory */
+       if( ! exporthtml_create_dir( _exportCtl_ ) ) {
+               reason = exporthtml_get_create_msg( _exportCtl_ );
+               msg = g_strdup_printf( _(
+                       "Could not create output directory for HTML file:\n%s" ),
+                       reason );
+               aval = alertpanel( _( "Failed to Create Directory" ),
+                       msg, _( "Close" ), NULL, NULL );
+               g_free( msg );
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+/*
+ * Move off format page.
+ * return: TRUE if OK to move off page.
+ */
+static gboolean exp_html_move_format() {
+       GtkWidget *menu, *menuItem;
+       gint id;
+
+       /* Set stylesheet */
+       menu = gtk_option_menu_get_menu( GTK_OPTION_MENU( exphtml_dlg.optmenuCSS ) );
+       menuItem = gtk_menu_get_active( GTK_MENU( menu ) );
+       id = GPOINTER_TO_INT( gtk_object_get_user_data(GTK_OBJECT(menuItem)) );
+       exporthtml_set_stylesheet( _exportCtl_, id );
+
+       /* Set name format */
+       menu = gtk_option_menu_get_menu( GTK_OPTION_MENU( exphtml_dlg.optmenuName ) );
+       menuItem = gtk_menu_get_active( GTK_MENU( menu ) );
+       id = GPOINTER_TO_INT( gtk_object_get_user_data(GTK_OBJECT(menuItem)) );
+       exporthtml_set_name_format( _exportCtl_, id );
+
+       exporthtml_set_banding( _exportCtl_,
+               gtk_toggle_button_get_active(
+                       GTK_TOGGLE_BUTTON( exphtml_dlg.checkBanding ) ) );
+       exporthtml_set_link_email( _exportCtl_,
+               gtk_toggle_button_get_active(
+                       GTK_TOGGLE_BUTTON( exphtml_dlg.checkLinkEMail ) ) );
+       exporthtml_set_attributes( _exportCtl_,
+               gtk_toggle_button_get_active(
+                       GTK_TOGGLE_BUTTON( exphtml_dlg.checkAttributes ) ) );
+
+       /* Process export */
+       exporthtml_process( _exportCtl_, _addressCache_ );
+
+       return TRUE;
+}
+
+/*
+ * Display finish page.
+ */
+static void exp_html_finish_show() {
+       gtk_label_set_text( GTK_LABEL(exphtml_dlg.labelOutFile), _exportCtl_->path );
+       gtk_widget_set_sensitive( exphtml_dlg.btnNext, FALSE );
+       gtk_widget_grab_focus( exphtml_dlg.btnCancel );
+}
+
+/*
+ * Previous button handler.
+ */
+static void export_html_prev( GtkWidget *widget ) {
+       gint pageNum;
+
+       pageNum = gtk_notebook_current_page( GTK_NOTEBOOK(exphtml_dlg.notebook) );
+       if( pageNum == PAGE_FORMAT ) {
+               /* Goto file page stuff */
+               gtk_notebook_set_page(
+                       GTK_NOTEBOOK(exphtml_dlg.notebook), PAGE_FILE_INFO );
+               gtk_widget_set_sensitive( exphtml_dlg.btnPrev, FALSE );
+       }
+       else if( pageNum == PAGE_FINISH ) {
+               /* Goto format page */
+               gtk_notebook_set_page(
+                       GTK_NOTEBOOK(exphtml_dlg.notebook), PAGE_FORMAT );
+               gtk_widget_set_sensitive( exphtml_dlg.btnNext, TRUE );
+       }
+       export_html_message();
+}
+
+/*
+ * Next button handler.
+ */
+static void export_html_next( GtkWidget *widget ) {
+       gint pageNum;
+
+       pageNum = gtk_notebook_current_page( GTK_NOTEBOOK(exphtml_dlg.notebook) );
+       if( pageNum == PAGE_FILE_INFO ) {
+               /* Goto format page */
+               if( exp_html_move_file() ) {
+                       gtk_notebook_set_page(
+                               GTK_NOTEBOOK(exphtml_dlg.notebook), PAGE_FORMAT );
+                       gtk_widget_set_sensitive( exphtml_dlg.btnPrev, TRUE );
+               }
+       }
+       else if( pageNum == PAGE_FORMAT ) {
+               /* Goto finish page */
+               if( exp_html_move_format() ) {
+                       gtk_notebook_set_page(
+                               GTK_NOTEBOOK(exphtml_dlg.notebook), PAGE_FINISH );
+                       exp_html_finish_show();
+                       exporthtml_save_settings( _exportCtl_ );
+               }
+       }
+       export_html_message();
+}
+
+/*
+ * Open file with web browser.
+ */
+static void export_html_browse( GtkWidget *widget, gpointer data ) {
+       gchar *uri;
+
+       uri = g_strconcat( "file://", _exportCtl_->path, NULL );
+       open_uri( uri, prefs_common.uri_cmd );
+       g_free( uri );
+}
+
+/*
+ * Output file - Ok.
+ */
+static void exp_html_file_ok( GtkWidget *widget, gpointer data ) {
+       gchar *sFile;
+       AddressFileSelection *afs;
+       GtkWidget *fileSel;
+
+       afs = ( AddressFileSelection * ) data;
+       fileSel = afs->fileSelector;
+       sFile = gtk_file_selection_get_filename( GTK_FILE_SELECTION(fileSel) );
+
+       afs->cancelled = FALSE;
+       gtk_entry_set_text( GTK_ENTRY(exphtml_dlg.entryHtml), sFile );
+       gtk_widget_hide( afs->fileSelector );
+       gtk_grab_remove( afs->fileSelector );
+       gtk_widget_grab_focus( exphtml_dlg.entryHtml );
+}
+
+/*
+ * Output file - Cancel.
+ */
+static void exp_html_file_cancel( GtkWidget *widget, gpointer data ) {
+       AddressFileSelection *afs = ( AddressFileSelection * ) data;
+       afs->cancelled = TRUE;
+       gtk_widget_hide( afs->fileSelector );
+       gtk_grab_remove( afs->fileSelector );
+       gtk_widget_grab_focus( exphtml_dlg.entryHtml );
+}
+
+/*
+ * Output file - Create.
+ */
+static void exp_html_file_select_create( AddressFileSelection *afs ) {
+       GtkWidget *fileSelector;
+
+       fileSelector = gtk_file_selection_new( _("Select HTML Output File") );
+       gtk_file_selection_hide_fileop_buttons( GTK_FILE_SELECTION(fileSelector) );
+       gtk_file_selection_complete( GTK_FILE_SELECTION(fileSelector), "*.html" );
+       gtk_signal_connect( GTK_OBJECT (GTK_FILE_SELECTION(fileSelector)->ok_button),
+               "clicked", GTK_SIGNAL_FUNC (exp_html_file_ok), ( gpointer ) afs );
+       gtk_signal_connect( GTK_OBJECT (GTK_FILE_SELECTION(fileSelector)->cancel_button),
+               "clicked", GTK_SIGNAL_FUNC (exp_html_file_cancel), ( gpointer ) afs );
+       afs->fileSelector = fileSelector;
+       afs->cancelled = TRUE;
+}
+
+/*
+ * Output file - Handle file selection.
+ */
+static void exp_html_file_select( void ) {
+       gchar *sFile;
+       if( ! _exp_html_file_selector_.fileSelector )
+               exp_html_file_select_create( & _exp_html_file_selector_ );
+
+       sFile = gtk_editable_get_chars( GTK_EDITABLE(exphtml_dlg.entryHtml), 0, -1 );
+       gtk_file_selection_set_filename(
+               GTK_FILE_SELECTION( _exp_html_file_selector_.fileSelector ),
+               sFile );
+       g_free( sFile );
+       gtk_widget_show( _exp_html_file_selector_.fileSelector );
+       gtk_grab_add( _exp_html_file_selector_.fileSelector );
+}
+
+static void export_html_page_file( gint pageNum, gchar *pageLbl ) {
+       GtkWidget *vbox;
+       GtkWidget *table;
+       GtkWidget *label;
+       GtkWidget *labelBook;
+       GtkWidget *entryHtml;
+       GtkWidget *btnFile;
+       gint top;
+
+       vbox = gtk_vbox_new(FALSE, 8);
+       gtk_container_add( GTK_CONTAINER( exphtml_dlg.notebook ), vbox );
+       gtk_container_set_border_width( GTK_CONTAINER (vbox), BORDER_WIDTH );
+
+       label = gtk_label_new( pageLbl );
+       gtk_widget_show( label );
+       gtk_notebook_set_tab_label(
+               GTK_NOTEBOOK( exphtml_dlg.notebook ),
+               gtk_notebook_get_nth_page(
+                       GTK_NOTEBOOK( exphtml_dlg.notebook ), pageNum ),
+               label );
+
+       table = gtk_table_new( 3, 3, FALSE );
+       gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
+       gtk_container_set_border_width( GTK_CONTAINER(table), 8 );
+       gtk_table_set_row_spacings(GTK_TABLE(table), 8);
+       gtk_table_set_col_spacings(GTK_TABLE(table), 8 );
+
+       /* First row */
+       top = 0;
+       label = gtk_label_new( _( "Address Book" ) );
+       gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1),
+               GTK_FILL, 0, 0, 0);
+       gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
+
+       labelBook = gtk_label_new( "Address book name goes here" );
+       gtk_table_attach(GTK_TABLE(table), labelBook, 1, 2, top, (top + 1),
+               GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
+       gtk_misc_set_alignment(GTK_MISC(labelBook), 0, 0.5);
+
+       /* Second row */
+       top++;
+       label = gtk_label_new( _( "HTML Output File" ) );
+       gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1),
+               GTK_FILL, 0, 0, 0);
+       gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
+
+       entryHtml = gtk_entry_new();
+       gtk_table_attach(GTK_TABLE(table), entryHtml, 1, 2, top, (top + 1),
+               GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
+
+       btnFile = gtk_button_new_with_label( _(" ... "));
+       gtk_table_attach(GTK_TABLE(table), btnFile, 2, 3, top, (top + 1),
+               GTK_FILL, 0, 3, 0);
+
+       gtk_widget_show_all(vbox);
+
+       /* Button handler */
+       gtk_signal_connect(GTK_OBJECT(btnFile), "clicked",
+                          GTK_SIGNAL_FUNC(exp_html_file_select), NULL);
+
+       exphtml_dlg.labelBook = labelBook;
+       exphtml_dlg.entryHtml = entryHtml;
+}
+
+static void export_html_page_format( gint pageNum, gchar *pageLbl ) {
+       GtkWidget *vbox;
+       GtkWidget *table;
+       GtkWidget *label;
+       GtkWidget *optmenuCSS;
+       GtkWidget *optmenuName;
+       GtkWidget *menu;
+       GtkWidget *menuItem;
+       GtkWidget *checkBanding;
+       GtkWidget *checkLinkEMail;
+       GtkWidget *checkAttributes;
+       gint top;
+
+       vbox = gtk_vbox_new(FALSE, 8);
+       gtk_container_add( GTK_CONTAINER( exphtml_dlg.notebook ), vbox );
+       gtk_container_set_border_width( GTK_CONTAINER (vbox), BORDER_WIDTH );
+
+       label = gtk_label_new( pageLbl );
+       gtk_widget_show( label );
+       gtk_notebook_set_tab_label(
+               GTK_NOTEBOOK( exphtml_dlg.notebook ),
+               gtk_notebook_get_nth_page(
+                       GTK_NOTEBOOK( exphtml_dlg.notebook ), pageNum ),
+               label );
+
+       table = gtk_table_new( 5, 3, FALSE );
+       gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
+       gtk_container_set_border_width( GTK_CONTAINER(table), 8 );
+       gtk_table_set_row_spacings(GTK_TABLE(table), 8);
+       gtk_table_set_col_spacings(GTK_TABLE(table), 8 );
+
+       /* First row */
+       top = 0;
+       label = gtk_label_new( _( "Stylesheet" ) );
+       gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1),
+               GTK_FILL, 0, 0, 0);
+       gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
+
+       menu = gtk_menu_new ();
+
+       menuItem = gtk_menu_item_new_with_label( _( "None" ) );
+       gtk_object_set_user_data( GTK_OBJECT( menuItem ),
+                       GINT_TO_POINTER( EXPORT_HTML_ID_NONE ) );
+       gtk_menu_append( GTK_MENU(menu), menuItem );
+       gtk_widget_show( menuItem );
+
+       menuItem = gtk_menu_item_new_with_label( _( "Default" ) );
+       gtk_object_set_user_data( GTK_OBJECT( menuItem ),
+                       GINT_TO_POINTER( EXPORT_HTML_ID_DEFAULT ) );
+       gtk_menu_append( GTK_MENU(menu), menuItem );
+       gtk_widget_show( menuItem );
+
+       menuItem = gtk_menu_item_new_with_label( _( "Full" ) );
+       gtk_object_set_user_data( GTK_OBJECT( menuItem ),
+                       GINT_TO_POINTER( EXPORT_HTML_ID_FULL ) );
+       gtk_menu_append( GTK_MENU(menu), menuItem );
+       gtk_widget_show( menuItem );
+
+       menuItem = gtk_menu_item_new_with_label( _( "Custom" ) );
+       gtk_object_set_user_data( GTK_OBJECT( menuItem ),
+                       GINT_TO_POINTER( EXPORT_HTML_ID_CUSTOM ) );
+       gtk_menu_append( GTK_MENU(menu), menuItem );
+       gtk_widget_show( menuItem );
+
+       menuItem = gtk_menu_item_new_with_label( _( "Custom-2" ) );
+       gtk_object_set_user_data( GTK_OBJECT( menuItem ),
+                       GINT_TO_POINTER( EXPORT_HTML_ID_CUSTOM2 ) );
+       gtk_menu_append( GTK_MENU(menu), menuItem );
+       gtk_widget_show( menuItem );
+
+       menuItem = gtk_menu_item_new_with_label( _( "Custom-3" ) );
+       gtk_object_set_user_data( GTK_OBJECT( menuItem ),
+                       GINT_TO_POINTER( EXPORT_HTML_ID_CUSTOM3 ) );
+       gtk_menu_append( GTK_MENU(menu), menuItem );
+       gtk_widget_show( menuItem );
+
+       menuItem = gtk_menu_item_new_with_label( _( "Custom-4" ) );
+       gtk_object_set_user_data( GTK_OBJECT( menuItem ),
+                       GINT_TO_POINTER( EXPORT_HTML_ID_CUSTOM4 ) );
+       gtk_menu_append( GTK_MENU(menu), menuItem );
+       gtk_widget_show( menuItem );
+
+       optmenuCSS = gtk_option_menu_new();
+       gtk_option_menu_set_menu( GTK_OPTION_MENU( optmenuCSS ), menu );
+
+       gtk_table_attach(GTK_TABLE(table), optmenuCSS, 1, 2, top, (top + 1),
+               GTK_FILL, 0, 0, 0);
+
+       /* Second row */
+       top++;
+       label = gtk_label_new( _( "Full Name Format" ) );
+       gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1),
+               GTK_FILL, 0, 0, 0);
+       gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
+
+       menu = gtk_menu_new ();
+
+       menuItem = gtk_menu_item_new_with_label( _( "First Name, Last Name" ) );
+       gtk_object_set_user_data( GTK_OBJECT( menuItem ),
+                       GINT_TO_POINTER( EXPORT_HTML_FIRST_LAST ) );
+       gtk_menu_append( GTK_MENU(menu), menuItem );
+       gtk_widget_show( menuItem );
+
+       menuItem = gtk_menu_item_new_with_label( _( "Last Name, First Name" ) );
+       gtk_object_set_user_data( GTK_OBJECT( menuItem ),
+                       GINT_TO_POINTER( EXPORT_HTML_LAST_FIRST ) );
+       gtk_menu_append( GTK_MENU(menu), menuItem );
+       gtk_widget_show( menuItem );
+
+       optmenuName = gtk_option_menu_new();
+       gtk_option_menu_set_menu( GTK_OPTION_MENU( optmenuName ), menu );
+
+       gtk_table_attach(GTK_TABLE(table), optmenuName, 1, 2, top, (top + 1),
+               GTK_FILL, 0, 0, 0);
+
+       /* Third row */
+       top++;
+       checkBanding = gtk_check_button_new_with_label( _( "Color Banding" ) );
+       gtk_table_attach(GTK_TABLE(table), checkBanding, 1, 2, top, (top + 1),
+               GTK_FILL, 0, 0, 0);
+
+       /* Fourth row */
+       top++;
+       checkLinkEMail = gtk_check_button_new_with_label( _( "Format E-Mail Links" ) );
+       gtk_table_attach(GTK_TABLE(table), checkLinkEMail, 1, 2, top, (top + 1),
+               GTK_FILL, 0, 0, 0);
+
+       /* Fifth row */
+       top++;
+       checkAttributes = gtk_check_button_new_with_label( _( "Format User Attributes" ) );
+       gtk_table_attach(GTK_TABLE(table), checkAttributes, 1, 2, top, (top + 1),
+               GTK_FILL, 0, 0, 0);
+
+       gtk_widget_show_all(vbox);
+
+       exphtml_dlg.optmenuCSS      = optmenuCSS;
+       exphtml_dlg.optmenuName     = optmenuName;
+       exphtml_dlg.checkBanding    = checkBanding;
+       exphtml_dlg.checkLinkEMail  = checkLinkEMail;
+       exphtml_dlg.checkAttributes = checkAttributes;
+}
+
+static void export_html_page_finish( gint pageNum, gchar *pageLbl ) {
+       GtkWidget *vbox;
+       GtkWidget *table;
+       GtkWidget *label;
+       GtkWidget *labelBook;
+       GtkWidget *labelFile;
+       GtkWidget *btnBrowse;
+       gint top;
+
+       vbox = gtk_vbox_new(FALSE, 8);
+       gtk_container_add( GTK_CONTAINER( exphtml_dlg.notebook ), vbox );
+       gtk_container_set_border_width( GTK_CONTAINER (vbox), BORDER_WIDTH );
+
+       label = gtk_label_new( pageLbl );
+       gtk_widget_show( label );
+       gtk_notebook_set_tab_label(
+               GTK_NOTEBOOK( exphtml_dlg.notebook ),
+               gtk_notebook_get_nth_page( GTK_NOTEBOOK( exphtml_dlg.notebook ), pageNum ), label );
+
+       table = gtk_table_new( 3, 3, FALSE );
+       gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
+       gtk_container_set_border_width( GTK_CONTAINER(table), 8 );
+       gtk_table_set_row_spacings(GTK_TABLE(table), 8);
+       gtk_table_set_col_spacings(GTK_TABLE(table), 8 );
+
+       /* First row */
+       top = 0;
+       label = gtk_label_new( _( "Address Book" ) );
+       gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), GTK_FILL, 0, 0, 0);
+       gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
+
+       labelBook = gtk_label_new("Full name of address book goes here");
+       gtk_table_attach(GTK_TABLE(table), labelBook, 1, 2, top, (top + 1), GTK_FILL, 0, 0, 0);
+       gtk_misc_set_alignment(GTK_MISC(labelBook), 0, 0.5);
+
+       /* Second row */
+       top++;
+       label = gtk_label_new( _( "File Name" ) );
+       gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), GTK_FILL, 0, 0, 0);
+       gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
+
+       labelFile = gtk_label_new("File name goes here");
+       gtk_table_attach(GTK_TABLE(table), labelFile, 1, 2, top, (top + 1), GTK_FILL, 0, 0, 0);
+       gtk_misc_set_alignment(GTK_MISC(labelFile), 0, 0.5);
+
+       /* Third row */
+       top++;
+       btnBrowse = gtk_button_new_with_label( _( "Open with Web Browser" ) );
+       gtk_table_attach(GTK_TABLE(table), btnBrowse, 1, 2, top, (top + 1), GTK_FILL, 0, 0, 0);
+
+       gtk_widget_show_all(vbox);
+
+       /* Button handlers */
+       gtk_signal_connect( GTK_OBJECT(btnBrowse), "clicked",
+               GTK_SIGNAL_FUNC(export_html_browse), NULL );
+
+       exphtml_dlg.labelOutBook = labelBook;
+       exphtml_dlg.labelOutFile = labelFile;
+}
+
+static void export_html_dialog_create() {
+       GtkWidget *window;
+       GtkWidget *vbox;
+       GtkWidget *vnbox;
+       GtkWidget *notebook;
+       GtkWidget *hbbox;
+       GtkWidget *btnPrev;
+       GtkWidget *btnNext;
+       GtkWidget *btnCancel;
+       GtkWidget *hsbox;
+       GtkWidget *statusbar;
+
+       window = gtk_window_new(GTK_WINDOW_DIALOG);
+       gtk_widget_set_usize(window, EXPORTHTML_WIDTH, EXPORTHTML_HEIGHT );
+       gtk_container_set_border_width( GTK_CONTAINER(window), 0 );
+       gtk_window_set_title( GTK_WINDOW(window),
+               _("Export Address Book to HTML File") );
+       gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
+       gtk_window_set_modal(GTK_WINDOW(window), TRUE); 
+       gtk_signal_connect(GTK_OBJECT(window), "delete_event",
+                          GTK_SIGNAL_FUNC(export_html_delete_event),
+                          NULL );
+       gtk_signal_connect(GTK_OBJECT(window), "key_press_event",
+                          GTK_SIGNAL_FUNC(export_html_key_pressed),
+                          NULL );
+
+       vbox = gtk_vbox_new(FALSE, 4);
+       gtk_widget_show(vbox);
+       gtk_container_add(GTK_CONTAINER(window), vbox);
+
+       vnbox = gtk_vbox_new(FALSE, 4);
+       gtk_container_set_border_width(GTK_CONTAINER(vnbox), 4);
+       gtk_widget_show(vnbox);
+       gtk_box_pack_start(GTK_BOX(vbox), vnbox, TRUE, TRUE, 0);
+
+       /* Notebook */
+       notebook = gtk_notebook_new();
+       gtk_notebook_set_show_tabs( GTK_NOTEBOOK(notebook), FALSE );
+       /* gtk_notebook_set_show_tabs( GTK_NOTEBOOK(notebook), TRUE ); */
+       gtk_widget_show(notebook);
+       gtk_box_pack_start(GTK_BOX(vnbox), notebook, TRUE, TRUE, 0);
+       gtk_container_set_border_width(GTK_CONTAINER(notebook), 6);
+
+       /* Status line */
+       hsbox = gtk_hbox_new(FALSE, 0);
+       gtk_box_pack_end(GTK_BOX(vbox), hsbox, FALSE, FALSE, BORDER_WIDTH);
+       statusbar = gtk_statusbar_new();
+       gtk_box_pack_start(GTK_BOX(hsbox), statusbar, TRUE, TRUE, BORDER_WIDTH);
+
+       /* Button panel */
+       gtkut_button_set_create(&hbbox, &btnPrev, _( "Prev" ),
+                               &btnNext, _( "Next" ),
+                               &btnCancel, _( "Cancel" ) );
+       gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
+       gtk_container_set_border_width(GTK_CONTAINER(hbbox), 2);
+       gtk_widget_grab_default(btnNext);
+
+       /* Button handlers */
+       gtk_signal_connect(GTK_OBJECT(btnPrev), "clicked",
+                          GTK_SIGNAL_FUNC(export_html_prev), NULL);
+       gtk_signal_connect(GTK_OBJECT(btnNext), "clicked",
+                          GTK_SIGNAL_FUNC(export_html_next), NULL);
+       gtk_signal_connect(GTK_OBJECT(btnCancel), "clicked",
+                          GTK_SIGNAL_FUNC(export_html_cancel), NULL);
+
+       gtk_widget_show_all(vbox);
+
+       exphtml_dlg.window     = window;
+       exphtml_dlg.notebook   = notebook;
+       exphtml_dlg.btnPrev    = btnPrev;
+       exphtml_dlg.btnNext    = btnNext;
+       exphtml_dlg.btnCancel  = btnCancel;
+       exphtml_dlg.statusbar  = statusbar;
+       exphtml_dlg.status_cid = gtk_statusbar_get_context_id(
+                       GTK_STATUSBAR(statusbar), "Export HTML Dialog" );
+
+}
+
+static void export_html_create() {
+       export_html_dialog_create();
+       export_html_page_file( PAGE_FILE_INFO, _( "File Info" ) );
+       export_html_page_format( PAGE_FORMAT, _( "Format" ) );
+       export_html_page_finish( PAGE_FINISH, _( "Finish" ) );
+       gtk_widget_show_all( exphtml_dlg.window );
+}
+
+/*
+ * Populate fields from control data.
+ * Enter:  ctl   Export control data.
+ */
+static void export_html_fill_fields( ExportHtmlCtl *ctl ) {
+       gtk_entry_set_text( GTK_ENTRY(exphtml_dlg.entryHtml), "" );
+       if( ctl->path ) {
+               gtk_entry_set_text( GTK_ENTRY(exphtml_dlg.entryHtml),
+                       ctl->path );
+       }
+
+       gtk_option_menu_set_history(
+               GTK_OPTION_MENU( exphtml_dlg.optmenuCSS ), ctl->stylesheet );
+       gtk_option_menu_set_history(
+               GTK_OPTION_MENU( exphtml_dlg.optmenuName ), ctl->nameFormat );
+       gtk_toggle_button_set_active(
+               GTK_TOGGLE_BUTTON( exphtml_dlg.checkBanding ), ctl->banding );
+       gtk_toggle_button_set_active(
+               GTK_TOGGLE_BUTTON( exphtml_dlg.checkLinkEMail ), ctl->linkEMail );
+       gtk_toggle_button_set_active(
+               GTK_TOGGLE_BUTTON( exphtml_dlg.checkAttributes ), ctl->showAttribs );
+}
+
+/*
+ * Process export address dialog.
+ * Enter: cache Address book/data source cache.
+ */
+void addressbook_exp_html( AddressCache *cache ) {
+       /* Set references to control data */
+       _addressCache_ = cache;
+
+       _exportCtl_ = exporthtml_create();
+       exporthtml_load_settings( _exportCtl_ );
+
+       /* Setup GUI */
+       if( ! exphtml_dlg.window )
+               export_html_create();
+       exphtml_dlg.cancelled = FALSE;
+       gtk_widget_show(exphtml_dlg.window);
+       manage_window_set_transient(GTK_WINDOW(exphtml_dlg.window));
+
+       gtk_label_set_text( GTK_LABEL(exphtml_dlg.labelBook), cache->name );
+       gtk_label_set_text( GTK_LABEL(exphtml_dlg.labelOutBook), cache->name );
+       export_html_fill_fields( _exportCtl_ );
+
+       gtk_widget_grab_default(exphtml_dlg.btnNext);
+       gtk_notebook_set_page( GTK_NOTEBOOK(exphtml_dlg.notebook), PAGE_FILE_INFO );
+       gtk_widget_set_sensitive( exphtml_dlg.btnPrev, FALSE );
+       gtk_widget_set_sensitive( exphtml_dlg.btnNext, TRUE );
+
+       export_html_message();
+       gtk_widget_grab_focus(exphtml_dlg.entryHtml);
+
+       gtk_main();
+       gtk_widget_hide(exphtml_dlg.window);
+       exporthtml_free( _exportCtl_ );
+       _exportCtl_ = NULL;
+
+       _addressCache_ = NULL;
+}
+
+/*
+* End of Source.
+*/
+
diff --git a/src/exphtmldlg.h b/src/exphtmldlg.h
new file mode 100644 (file)
index 0000000..26addaf
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 2002 Match Grun
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * Export addressbook to HTML file.
+ */
+
+#ifndef __EXPORT_HTML_H__
+#define __EXPORT_HTML_H__
+
+/* Function prototypes */
+void addressbook_exp_html( AddressCache *cache );
+
+#endif /* __EXPORT_HTML_H__ */
+
+/*
+* End of Source.
+*/
+
diff --git a/src/exporthtml.c b/src/exporthtml.c
new file mode 100644 (file)
index 0000000..67d76ad
--- /dev/null
@@ -0,0 +1,1228 @@
+/*
+ * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 2002 Match Grun
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * Export address book to HTML file.
+ */
+
+#include <sys/stat.h>
+#include <dirent.h>
+#include <errno.h>
+#include <time.h>
+#include <string.h>
+#include <glib.h>
+
+#include "intl.h"
+#include "mgutils.h"
+#include "utils.h"
+#include "exporthtml.h"
+#include "addritem.h"
+#include "addrcache.h"
+#include "addrbook.h"
+#include "xmlprops.h"
+
+#define DFL_DIR_SYLPHEED_OUT  "sylpheed-out"
+#define DFL_FILE_SYLPHEED_OUT "addressbook.html"
+
+#define FMT_BUFSIZE         2048
+#define HTML_SPACE          "&nbsp;"
+#define BORDER_SIZE         2
+#define CELL_PADDING        2
+#define CELL_SPACING        2
+#define CHAR_ENCODING       "ISO-8859-1"
+
+/* Stylesheet names */
+#define FILENAME_NONE       ""
+#define FILENAME_DEFAULT    "sylpheed.css"
+#define FILENAME_FULL       "full.css"
+#define FILENAME_CUSTOM     "custom.css"
+#define FILENAME_CUSTOM2    "custom2.css"
+#define FILENAME_CUSTOM3    "custom3.css"
+#define FILENAME_CUSTOM4    "custom4.css"
+
+/* Settings - properties */
+#define EXML_PROPFILE_NAME  "exporthtml.xml"
+#define EXMLPROP_DIRECTORY  "directory"
+#define EXMLPROP_FILE       "file"
+#define EXMLPROP_STYLESHEET "stylesheet"
+#define EXMLPROP_FMT_NAME   "format-full-name"
+#define EXMLPROP_FMT_EMAIL  "format-email-links"
+#define EXMLPROP_FMT_ATTRIB "format-attributes"
+#define EXMLPROP_BANDING    "color-banding"
+#define EXMLPROP_VALUE_YES  "y"
+#define EXMLPROP_VALUE_NO   "n"
+
+static gchar *_idTagRowEven_ = "tab-row0";
+static gchar *_idTagRowOdd_  = "tab-row1";
+
+/*
+ * Header entry.
+ */
+typedef struct _StylesheetEntry StylesheetEntry;
+struct _StylesheetEntry {
+       gchar    *fileName;
+       gint     id;
+       gboolean dflValue;
+};
+
+/*
+ * Build stylesheet entry.
+ * Enter: ctl   Export control data.
+ *        file  Filename.
+ *        id    File id.
+ *        dfl   Default flag.
+ */
+static void exporthtml_build_entry(
+               ExportHtmlCtl *ctl, const gchar *file, const gint id,
+               const gboolean dfl )
+{
+       StylesheetEntry *entry;
+
+       entry = g_new0( StylesheetEntry, 1 );
+       entry->fileName = g_strdup( file );
+       entry->id = id;
+       entry->dflValue = dfl;
+       ctl->listStyle = g_list_append( ctl->listStyle, entry );
+}
+
+/*
+ * Free up object by releasing internal memory.
+ * Enter: ctl Export control data.
+ */
+ExportHtmlCtl *exporthtml_create( void ) {
+       ExportHtmlCtl *ctl = g_new0( ExportHtmlCtl, 1 );
+
+       ctl->path = NULL;
+       ctl->dirOutput = NULL;
+       ctl->fileHtml = NULL;
+       ctl->encoding = NULL;
+       ctl->stylesheet = EXPORT_HTML_ID_NONE;
+       ctl->nameFormat = EXPORT_HTML_FIRST_LAST;
+       ctl->banding = FALSE;
+       ctl->linkEMail = FALSE;
+       ctl->showAttribs = FALSE;
+       ctl->retVal = MGU_SUCCESS;
+       ctl->listStyle = NULL;
+       ctl->rcCreate = 0;
+       ctl->settingsFile = g_strconcat(
+               get_rc_dir(), G_DIR_SEPARATOR_S, EXML_PROPFILE_NAME, NULL );
+
+       /* Build stylesheet list */
+       exporthtml_build_entry(
+               ctl, FILENAME_NONE,    EXPORT_HTML_ID_NONE, FALSE );
+       exporthtml_build_entry(
+               ctl, FILENAME_DEFAULT, EXPORT_HTML_ID_DEFAULT, TRUE );
+       exporthtml_build_entry(
+               ctl, FILENAME_FULL,    EXPORT_HTML_ID_FULL, FALSE );
+       exporthtml_build_entry(
+               ctl, FILENAME_CUSTOM,  EXPORT_HTML_ID_CUSTOM, FALSE );
+       exporthtml_build_entry(
+               ctl, FILENAME_CUSTOM2, EXPORT_HTML_ID_CUSTOM2, FALSE );
+       exporthtml_build_entry(
+               ctl, FILENAME_CUSTOM3, EXPORT_HTML_ID_CUSTOM3, FALSE );
+       exporthtml_build_entry(
+               ctl, FILENAME_CUSTOM4, EXPORT_HTML_ID_CUSTOM4, FALSE );
+
+       return ctl;
+}
+
+/*
+ * Free up object by releasing internal memory.
+ * Enter: ctl Export control data.
+ */
+void exporthtml_free( ExportHtmlCtl *ctl ) {
+       GList *node;
+       StylesheetEntry *entry;
+
+       g_return_if_fail( ctl != NULL );
+
+       /* Free stylesheet list */
+       node = ctl->listStyle;
+       while( node ) {
+               entry = ( StylesheetEntry * ) node->data;
+               g_free( entry->fileName );
+               entry->fileName = NULL;
+               entry->id = 0;
+               entry->dflValue = FALSE;
+               g_free( entry );
+               node->data = NULL;
+               node = g_list_next( node );
+       }
+       g_list_free( ctl->listStyle );
+       ctl->listStyle = NULL;
+
+       g_free( ctl->path );
+       g_free( ctl->fileHtml );
+       g_free( ctl->encoding );
+       g_free( ctl->dirOutput );
+       g_free( ctl->settingsFile );
+
+       /* Clear pointers */
+       ctl->path = NULL;
+       ctl->dirOutput = NULL;
+       ctl->fileHtml = NULL;
+       ctl->encoding = NULL;
+       ctl->stylesheet = EXPORT_HTML_ID_NONE;
+       ctl->nameFormat = EXPORT_HTML_FIRST_LAST;
+       ctl->banding = FALSE;
+       ctl->linkEMail = FALSE;
+       ctl->showAttribs = FALSE;
+       ctl->retVal = MGU_SUCCESS;
+       ctl->rcCreate = 0;
+
+       /* Now release object */
+       g_free( ctl );
+}
+
+/*
+ * Print control object.
+ * Enter: ctl    Export control data.
+ *        stream Output stream.
+ */
+void exporthtml_print( ExportHtmlCtl *ctl, FILE *stream ) {
+       fprintf( stream, "ExportHtmlCtl:\n" );
+       fprintf( stream, "     path: %s\n", ctl->path );
+       fprintf( stream, "directory: %s\n", ctl->dirOutput );
+       fprintf( stream, "     file: %s\n", ctl->fileHtml );
+       fprintf( stream, " settings: %s\n", ctl->settingsFile );
+       fprintf( stream, " encoding: %s\n", ctl->encoding );
+       fprintf( stream, " css file: %d\n", ctl->stylesheet );
+       fprintf( stream, " name fmt: %d\n", ctl->nameFormat );
+       fprintf( stream, "  banding: %d\n", ctl->banding );
+       fprintf( stream, "link mail: %d\n", ctl->linkEMail );
+       fprintf( stream, "show attr: %d\n", ctl->showAttribs );
+}
+
+/*
+ * Find style entry.
+ * Enter: ctl Export control data.
+ * Return: Stylesheet object, or NULL if nothing found. If a default entry is
+ * found in list, it will be returned.
+ */
+static StylesheetEntry *exporthtml_find_stylesheet( ExportHtmlCtl *ctl ) {
+       StylesheetEntry *retVal;
+       StylesheetEntry *entry;
+       GList *node;
+
+       node = ctl->listStyle;
+       while( node ) {
+               entry = ( StylesheetEntry * ) node->data;
+               if( entry->id == ctl->stylesheet ) return entry;
+               if( entry->dflValue ) retVal = entry;
+               node = g_list_next( node );
+       }
+       return retVal;
+}
+
+/*
+ * Properties...
+ */
+/*
+ * Specify path to folder where files are created.
+ * Enter: ctl   Export control data.
+ *        value Full directory path.
+ */
+void exporthtml_set_output_dir( ExportHtmlCtl *ctl, const gchar *value ) {
+       g_return_if_fail( ctl != NULL );
+       ctl->dirOutput = mgu_replace_string( ctl->dirOutput, value );
+       g_strstrip( ctl->dirOutput );
+}
+void exporthtml_set_path( ExportHtmlCtl *ctl, const gchar *value ) {
+       g_return_if_fail( ctl != NULL );
+       ctl->path = mgu_replace_string( ctl->path, value );
+       g_strstrip( ctl->path );
+}
+void exporthtml_set_file_html( ExportHtmlCtl *ctl, const gchar *value ) {
+       g_return_if_fail( ctl != NULL );
+       ctl->fileHtml = mgu_replace_string( ctl->fileHtml, value );
+       g_strstrip( ctl->fileHtml );
+}
+void exporthtml_set_encoding( ExportHtmlCtl *ctl, const gchar *value ) {
+       g_return_if_fail( ctl != NULL );
+       ctl->encoding = mgu_replace_string( ctl->encoding, value );
+       g_strstrip( ctl->encoding );
+}
+void exporthtml_set_stylesheet( ExportHtmlCtl *ctl, const gint value ) {
+       g_return_if_fail( ctl != NULL );
+       ctl->stylesheet = value;
+}
+void exporthtml_set_name_format( ExportHtmlCtl *ctl, const gint value ) {
+       g_return_if_fail( ctl != NULL );
+       ctl->nameFormat = value;
+}
+void exporthtml_set_banding( ExportHtmlCtl *ctl, const gboolean value ) {
+       g_return_if_fail( ctl != NULL );
+       ctl->banding = value;
+}
+void exporthtml_set_link_email( ExportHtmlCtl *ctl, const gboolean value ) {
+       g_return_if_fail( ctl != NULL );
+       ctl->linkEMail = value;
+}
+void exporthtml_set_attributes( ExportHtmlCtl *ctl, const gboolean value ) {
+       g_return_if_fail( ctl != NULL );
+       ctl->showAttribs = value;
+}
+
+/*
+ * Create default CSS file.
+ * Enter:  fileSpec File to create.
+ * Return: Status code.
+ */
+static gint exporthtml_create_css_dfl( const gchar *fileSpec ) {
+       FILE *cssFile;
+
+       cssFile = fopen( fileSpec, "rb" );
+       if( cssFile ) {
+               fclose( cssFile );
+               return MGU_SUCCESS;
+       }
+       cssFile = fopen( fileSpec, "wb" );
+       if( ! cssFile ) {
+               return MGU_OPEN_FILE;
+       }
+
+       fprintf( cssFile, "body {\n\tbackground: #ffffe0;\n" );
+       fprintf( cssFile, "\tfont-family: lucida, helvetica, sans-serif;\n" );
+       fprintf( cssFile, "\tfont-size: 10pt;\n" );
+       fprintf( cssFile, "}\n" );
+       fprintf( cssFile, "h1 {\n" );
+       fprintf( cssFile, "\tcolor: #000000;\n" );
+       fprintf( cssFile, "\ttext-align: center;\n" );
+       fprintf( cssFile, "}\n" );
+       fprintf( cssFile, "th {\n" );
+       fprintf( cssFile, "\tfont-size: 10pt;\n" );
+       fprintf( cssFile, "}\n" );
+       fprintf( cssFile, "td {\n" );
+       fprintf( cssFile, "\tfont-size: 10pt;\n" );
+       fprintf( cssFile, "}\n" );
+       fprintf( cssFile, ".fmt-folder {\n" );
+       fprintf( cssFile, "\tcolor: #0000ff;\n" );
+       fprintf( cssFile, "\tfont-size: 18pt;\n" );
+       fprintf( cssFile, "\tfont-weight: bold;\n" );
+       fprintf( cssFile, "}\n" );
+       fprintf( cssFile, ".tab-head {\n" );
+       fprintf( cssFile, "\tbackground: #80c0f0;\n" );
+       fprintf( cssFile, "}\n" );
+       fprintf( cssFile, ".tab-dn {\n" );
+       fprintf( cssFile, "}\n" );
+       fprintf( cssFile, ".tab-addr {\n" );
+       fprintf( cssFile, "\tfont-style: italic;\n" );
+       fprintf( cssFile, "}\n" );
+       fprintf( cssFile, ".tab-email {\n" );
+       fprintf( cssFile, "\tfont-weight: bold;\n" );
+       fprintf( cssFile, "\tfont-style: italic;\n" );
+       fprintf( cssFile, "}\n" );
+       fprintf( cssFile, ".tab-fn {\n" );
+       fprintf( cssFile, "}\n" );
+       fprintf( cssFile, ".tab-attr {\n" );
+       fprintf( cssFile, "}\n" );
+
+       fclose( cssFile );
+       return MGU_SUCCESS;
+}
+
+/*
+ * Create full CSS file.
+ * Enter:  fileSpec File to create.
+ * Return: Status code.
+ */
+static gint exporthtml_create_css_full( const gchar *fileSpec ) {
+       FILE *cssFile;
+
+       cssFile = fopen( fileSpec, "rb" );
+       if( cssFile ) {
+               fclose( cssFile );
+               return MGU_SUCCESS;
+       }
+       cssFile = fopen( fileSpec, "wb" );
+       if( ! cssFile ) {
+               return MGU_OPEN_FILE;
+       }
+
+       fprintf( cssFile, "body {\n\tbackground: #ffffe0;\n" );
+       fprintf( cssFile, "\tfont-family: lucida, helvetica, sans-serif;\n" );
+       fprintf( cssFile, "\tfont-size: 10pt;\n" );
+       fprintf( cssFile, "}\n" );
+       fprintf( cssFile, "h1 {\n" );
+       fprintf( cssFile, "\tcolor: #000000;\n" );
+       fprintf( cssFile, "\ttext-align: center;\n" );
+       fprintf( cssFile, "}\n" );
+       fprintf( cssFile, "th {\n" );
+       fprintf( cssFile, "\tfont-size: 10pt;\n" );
+       fprintf( cssFile, "}\n" );
+       fprintf( cssFile, "td {\n" );
+       fprintf( cssFile, "\tfont-size: 10pt;\n" );
+       fprintf( cssFile, "}\n" );
+       fprintf( cssFile, ".fmt-folder {\n" );
+       fprintf( cssFile, "\tcolor: #0000ff;\n" );
+       fprintf( cssFile, "\tfont-size: 18pt;\n" );
+       fprintf( cssFile, "\tfont-weight: bold;\n" );
+       fprintf( cssFile, "}\n" );
+       fprintf( cssFile, ".tab-head {\n" );
+       fprintf( cssFile, "\tbackground: #80c0f0;\n" );
+       fprintf( cssFile, "}\n" );
+       fprintf( cssFile, ".tab-row0 {\n" );
+       fprintf( cssFile, "\tbackground: #f0f0f0;\n" );
+       fprintf( cssFile, "}\n" );
+       fprintf( cssFile, ".tab-row1 {\n" );
+       fprintf( cssFile, "\tbackground: #d0d0d0;\n" );
+       fprintf( cssFile, "}\n" );
+       fprintf( cssFile, ".tab-dn {\n" );
+       fprintf( cssFile, "}\n" );
+       fprintf( cssFile, ".tab-addr {\n" );
+       fprintf( cssFile, "\tfont-style: italic;\n" );
+       fprintf( cssFile, "}\n" );
+       fprintf( cssFile, ".tab-email {\n" );
+       fprintf( cssFile, "\tfont-weight: bold;\n" );
+       fprintf( cssFile, "\tfont-style: italic;\n" );
+       fprintf( cssFile, "}\n" );
+       fprintf( cssFile, ".tab-fn {\n" );
+       fprintf( cssFile, "}\n" );
+       fprintf( cssFile, ".tab-attr {\n" );
+       fprintf( cssFile, "}\n" );
+
+       fclose( cssFile );
+       return MGU_SUCCESS;
+}
+
+/*
+ * Create stylesheet files.
+ * Enter:  ctl  Export control data.
+ */
+static void exporthtml_create_css_files( ExportHtmlCtl *ctl ) {
+       gchar *fileSpec;
+       GList *node;
+
+       node = ctl->listStyle;
+       while( node ) {
+               StylesheetEntry *entry = node->data;
+               node = g_list_next( node );
+               if( strlen( entry->fileName ) ) {
+                       fileSpec = g_strconcat(
+                                       ctl->dirOutput, G_DIR_SEPARATOR_S, 
+                                       entry->fileName, NULL );
+                       if( entry->id == EXPORT_HTML_ID_DEFAULT ) {
+                               exporthtml_create_css_dfl( fileSpec );
+                       }
+                       else if( entry->id != EXPORT_HTML_ID_NONE ) {
+                               exporthtml_create_css_full( fileSpec );
+                       }
+                       g_free( fileSpec );
+               }
+       }
+}
+
+/*
+ * Comparison using linked list elements.
+ */
+static gint exporthtml_compare_name(
+       gconstpointer ptr1, gconstpointer ptr2 )
+{
+       const AddrItemObject *item1 = ptr1;
+       const AddrItemObject *item2 = ptr2;
+       const gchar *name1 = NULL, *name2 = NULL;
+       if( item1 ) name1 = ADDRITEM_NAME( item1 );
+       if( item2 ) name2 = ADDRITEM_NAME( item2 );
+       if( ! name1 ) return ( name2 != NULL );
+       if( ! name2 ) return -1;
+       return strcasecmp( name1, name2 );
+}
+
+/*
+ * Comparison using linked list elements.
+ */
+static gint exporthtml_compare_email(
+       gconstpointer ptr1, gconstpointer ptr2 )
+{
+       const ItemEMail *email1 = ptr1;
+       const ItemEMail *email2 = ptr2;
+       const gchar *name1 = NULL, *name2 = NULL;
+       if( email1 ) name1 = email1->address;
+       if( email2 ) name2 = email2->address;
+       if( ! name1 ) return ( name2 != NULL );
+       if( ! name2 ) return -1;
+       return strcasecmp( name1, name2 );
+}
+
+/*
+ * Comparison using linked list elements.
+ */
+static gint exporthtml_compare_attrib(
+       gconstpointer ptr1, gconstpointer ptr2 )
+{
+       const UserAttribute *attr1 = ptr1;
+       const UserAttribute *attr2 = ptr2;
+       const gchar *name1 = NULL, *name2 = NULL;
+       if( attr1 ) name1 = attr1->name;
+       if( attr2 ) name2 = attr2->name;
+       if( ! name1 ) return ( name2 != NULL );
+       if( ! name2 ) return -1;
+       return strcasecmp( name1, name2 );
+}
+
+/*
+ * Build sorted list of named items.
+ * Enter:  list  List of items to sorted.
+ * Return: Sorted list.
+ * Note: List should freed after use. Items referenced by list should not be
+ * freed since they are managed by the address cache.
+ */
+static GList *exporthtml_sort_name( const GList *list ) {
+       const GList *node;
+       GList *sorted = NULL;
+
+       node = list;
+       while( node ) {
+               sorted = g_list_insert_sorted(
+                               sorted, node->data, exporthtml_compare_name );
+               node = g_list_next( node );
+       }
+       return sorted;
+}
+
+/*
+ * Build sorted list of email items.
+ * Enter:  list  List of E-Mail items to sorted.
+ * Return: Sorted list.
+ * Note: List should freed after use. Items referenced by list should not be
+ * freed since they are managed by the address cache.
+ */
+static GList *exporthtml_sort_email( const GList *list ) {
+       const GList *node;
+       GList *sorted = NULL;
+
+       node = list;
+       while( node ) {
+               sorted = g_list_insert_sorted(
+                               sorted, node->data, exporthtml_compare_email );
+               node = g_list_next( node );
+       }
+       return sorted;
+}
+
+/*
+ * Build sorted list of attributes.
+ * Enter:  list  List of items to sorted.
+ * Return: Sorted list.
+ * Note: List should freed after use. Items referenced by list should not be
+ * freed since they are managed by the address cache.
+ */
+static GList *exporthtml_sort_attrib( const GList *list ) {
+       const GList *node;
+       GList *sorted = NULL;
+
+       sorted = NULL;
+       node = list;
+       while( node ) {
+               sorted = g_list_insert_sorted(
+                               sorted, node->data, exporthtml_compare_attrib );
+               node = g_list_next( node );
+       }
+       return sorted;
+}
+
+/*
+ * Format a list of E-Mail addresses.
+ * Enter: ctl       Export control data.
+ *        stream    Output stream.
+ *        listEMail List of addresses.
+ *        sortFlag  Set to TRUE if address list should be sorted.
+ */
+static void exporthtml_fmt_email(
+               ExportHtmlCtl *ctl, FILE *stream, const GList *listEMail,
+               gboolean sortFlag )
+{
+       const GList *node;
+       GList *list;
+       gchar *name;
+
+       if( listEMail == NULL ) {
+               fprintf( stream, HTML_SPACE );
+               return;
+       }
+
+       list = NULL;
+       if( sortFlag ) {
+               node = list = exporthtml_sort_email( listEMail );
+       }
+       else {
+               node = listEMail;
+       }
+
+       while( node ) {
+               ItemEMail *email = ( ItemEMail * ) node->data;
+               node = g_list_next( node );
+
+               name = ADDRITEM_NAME( email );
+               if( name ) {
+                       fprintf( stream, "%s ", name );
+               }
+               if( ctl->linkEMail ) {
+                       fprintf( stream, "<a href=\"mailto:%s\">",
+                               email->address );
+               }
+               fprintf( stream, "<span class=\"tab-email\">" );
+               fprintf( stream, "%s", email->address );
+               fprintf( stream, "</span>" );
+               if( ctl->linkEMail ) {
+                       fprintf( stream, "</a>" );
+               }
+               if( email->remarks ) {
+                       if( strlen( email->remarks ) ) {
+                               fprintf( stream, " (%s)", email->remarks );
+                       }
+               }
+               fprintf( stream, "<br>\n" );
+       }
+       g_list_free( list );
+}
+
+/*
+ * Format groups in an address book folder.
+ * Enter:  ctl      Export control data.
+ *         stream   Output stream.
+ *         folder   Folder.
+ *         prevFlag If FALSE, list of persons were output.
+ * Return: TRUE if no groups were formatted.
+ */
+static gboolean exporthtml_fmt_group(
+               ExportHtmlCtl *ctl, FILE *stream, const ItemFolder *folder,
+               gboolean prevFlag )
+{
+       gboolean retVal, band;
+       GList *node, *list;
+       const gchar *tagName;
+
+       retVal = TRUE;
+       if( folder->listGroup == NULL ) return retVal;
+
+       /* Write separator */
+       if( ! prevFlag ) {
+               fprintf( stream, "<br>\n" );
+       }
+
+       /* Write table headers */
+       fprintf( stream, "<table" );
+       fprintf( stream, " border=\"%d\"", BORDER_SIZE );
+       fprintf( stream, " cellpadding=\"%d\"", CELL_PADDING );
+       fprintf( stream, " cellspacing=\"%d\"", CELL_SPACING );
+       fprintf( stream, ">\n" );
+
+       fprintf( stream, "<tr class=\"tab-head\">\n" );
+       fprintf( stream, "  <th width=\"200\">" );
+       fprintf( stream, "%s", _( "Group Name" ) );
+       fprintf( stream, "</th>\n" );
+       fprintf( stream, "  <th width=\"300\">" );
+       fprintf( stream, "%s", _( "E-Mail Address" ) );
+       fprintf( stream, "</th>\n" );
+       fprintf( stream, "</tr>\n" );
+       list = exporthtml_sort_name( folder->listGroup );
+
+       band = FALSE;
+       node = list;
+       while( node ) {
+               AddrItemObject *aio = node->data;
+               if( aio && aio->type == ITEMTYPE_GROUP ) {
+                       ItemGroup *group = ( ItemGroup * ) aio;
+
+                       fprintf( stream, "<tr valign=\"top\"" );
+                       if( ctl->banding ) {
+                               if( band ) {
+                                       tagName = _idTagRowOdd_;
+                               }
+                               else {
+                                       tagName = _idTagRowEven_;
+                               }
+                               fprintf( stream, " class=\"%s\"", tagName );
+                               band = ! band;
+                       }
+                       fprintf( stream, "\">\n" );
+
+                       fprintf( stream, "  <td class=\"tab-dn\">" );
+                       fprintf( stream, "%s", ADDRITEM_NAME( group ) );
+                       fprintf( stream, "</td>\n" );
+                       fprintf( stream, "  <td class=\"tab-addr\">" );
+                       exporthtml_fmt_email( ctl, stream, group->listEMail, TRUE );
+                       fprintf( stream, "</td>\n" );
+                       fprintf( stream, "</tr>\n" );
+                       retVal = FALSE;
+               }
+               node = g_list_next( node );
+       }
+
+       g_list_free( list );
+       fprintf( stream, "</table>\n" );
+       return retVal;
+}
+
+/*
+ * Format a list of E-Mail addresses.
+ * Enter: ctl       Export control data.
+ *        stream    Output stream.
+ *        listAttr  List of attributes.
+ */
+static void exporthtml_fmt_attribs(
+               ExportHtmlCtl *ctl, FILE *stream, const GList *listAttr )
+{
+       const GList *node;
+       GList *list;
+
+       if( listAttr == NULL ) {
+               fprintf( stream, HTML_SPACE );
+               return;
+       }
+
+       fprintf( stream, "<table border=\"0\">\n" );
+       node = list = exporthtml_sort_attrib( listAttr );
+       while( node ) {
+               UserAttribute *attr = ( UserAttribute * ) node->data;
+               node = g_list_next( node );
+               fprintf( stream, "<tr valign=\"top\">" );
+               fprintf( stream, "<td align=\"right\">%s:</td>", attr->name );
+               fprintf( stream, "<td>%s</td>", attr->value );
+               fprintf( stream, "</tr>\n" );
+       }
+
+       g_list_free( list );
+       fprintf( stream, "</table>" );
+}
+
+/*
+ * Format full name.
+ * Enter:  ctl     Export control data.
+ *         buf     Output buffer.
+ *         person  Person to format.
+ */
+static void exporthtml_fmt_fullname(
+               ExportHtmlCtl *ctl, gchar *buf, const ItemPerson *person )
+{
+       gboolean flag;
+
+       if( ctl->nameFormat == EXPORT_HTML_LAST_FIRST ) {
+               flag = FALSE;
+               if( person->lastName ) {
+                       if( *person->lastName ) {
+                               strcat( buf, " " );
+                               strcat( buf, person->lastName );
+                               flag = TRUE;
+                       }
+               }
+               if( person->firstName ) {
+                       if( *person->firstName ) {
+                               if( flag ) {
+                                       strcat( buf, ", " );
+                               }
+                               strcat( buf, person->firstName );
+                       }
+               }
+       }
+       else {
+               if( person->firstName ) {
+                       if( *person->firstName ) {
+                               strcat( buf, person->firstName );
+                       }
+               }
+               if( person->lastName ) {
+                       if( *person->lastName ) {
+                               strcat( buf, " " );
+                               strcat( buf, person->lastName );
+                       }
+               }
+       }
+       g_strstrip( buf );
+
+       flag = FALSE;
+       if( *buf ) flag = TRUE;
+       if( person->nickName ) {
+               if( strlen( person->nickName ) ) {
+                       if( flag ) {
+                               strcat( buf, " (" );
+                       }
+                       strcat( buf, person->nickName );
+                       if( flag ) {
+                               strcat( buf, ")" );
+                       }
+               }
+       }
+       g_strstrip( buf );
+}
+
+/*
+ * Format persons in an address book folder.
+ * Enter:  ctl     Export control data.
+ *         stream  Output stream.
+ *         folder  Folder.
+ * Return: TRUE if no persons were formatted.
+ */
+static gboolean exporthtml_fmt_person(
+               ExportHtmlCtl *ctl, FILE *stream, const ItemFolder *folder )
+{
+       gboolean retVal, band;
+       GList *node, *list;
+       gchar buf[ FMT_BUFSIZE ];
+       const gchar *tagName;
+
+       retVal = TRUE;
+       if( folder->listPerson == NULL ) return retVal;
+
+       /* Write table headers */
+       fprintf( stream, "<table" );
+       fprintf( stream, " border=\"%d\"", BORDER_SIZE );
+       fprintf( stream, " cellpadding=\"%d\"", CELL_PADDING );
+       fprintf( stream, " cellspacing=\"%d\"", CELL_SPACING );
+       fprintf( stream, ">\n" );
+
+       fprintf( stream, "<tr class=\"tab-head\">\n" );
+       fprintf( stream, "  <th width=\"200\">" );
+       fprintf( stream, "%s", _( "Display Name" ) );
+       fprintf( stream, "</th>\n" );
+       fprintf( stream, "  <th width=\"300\">" );
+       fprintf( stream, "%s", _( "E-Mail Address" ) );
+       fprintf( stream, "</th>\n" );
+       fprintf( stream, "  <th width=\"200\">" );
+       fprintf( stream, "%s", _( "Full Name" ) );
+       fprintf( stream, "</th>\n" );
+       if( ctl->showAttribs ) {
+               fprintf( stream, "  <th width=\"250\">" );
+               fprintf( stream, "%s", _( "Attributes" ) );
+               fprintf( stream, "</th>\n" );
+       }
+       fprintf( stream, "</tr>\n" );
+
+       band = FALSE;
+       node = list = exporthtml_sort_name( folder->listPerson );
+       while( node ) {
+               AddrItemObject *aio = node->data;
+               if( aio && aio->type == ITEMTYPE_PERSON ) {
+                       ItemPerson *person = ( ItemPerson * ) aio;
+
+                       /* Format first/last/nick name */
+                       *buf = '\0';
+                       exporthtml_fmt_fullname( ctl, buf,person );
+
+                       fprintf( stream, "<tr valign=\"top\"" );
+                       if( ctl->banding ) {
+                               if( band ) {
+                                       tagName = _idTagRowOdd_;
+                               }
+                               else {
+                                       tagName = _idTagRowEven_;
+                               }
+                               fprintf( stream, " class=\"%s\"", tagName );
+                               band = ! band;
+                       }
+                       fprintf( stream, ">\n" );
+
+                       fprintf( stream, "  <td class=\"tab-dn\">" );
+                       fprintf( stream, "%s", ADDRITEM_NAME( person ) );
+                       fprintf( stream, "</td>\n" );
+
+                       fprintf( stream, "  <td class=\"tab-addr\">" );
+                       exporthtml_fmt_email( ctl, stream, person->listEMail, FALSE );
+                       fprintf( stream, "</td>\n" );
+
+                       fprintf( stream, "  <td class=\"tab-fn\">" );
+                       if( *buf ) {
+                               fprintf( stream, "%s", buf );
+                       }
+                       else {
+                               fprintf( stream, "%s", HTML_SPACE );
+                       }
+                       fprintf( stream, "</td>\n" );
+
+                       if( ctl->showAttribs ) {
+                               fprintf( stream, "  <td class=\"tab-attr\">" );
+                               exporthtml_fmt_attribs(
+                                       ctl, stream, person->listAttrib );
+                               fprintf( stream, "</td>\n" );
+                       }
+                       fprintf( stream, "</tr>\n" );
+
+                       retVal = FALSE;
+               }
+               node = g_list_next( node );
+       }
+
+       g_list_free( list );
+       fprintf( stream, "</table>\n" );
+       return retVal;
+}
+
+/*
+ * Format folder heirarchy.
+ * Enter: stream Output stream.
+ *        list   Heirarchy list.
+ */
+static void exporthtml_fmt_folderhead( FILE *stream, const GList *list ) {
+       const GList *node;
+       gboolean flag;
+       gchar *name;
+
+       flag = FALSE;
+       node = list;
+       while( node ) {
+               AddrItemObject *aio = node->data;
+               if( aio && aio->type == ITEMTYPE_FOLDER ) {
+                       ItemFolder *folder = ( ItemFolder * ) aio;
+
+                       name = ADDRITEM_NAME( folder );
+                       if( name ) {
+                               if( flag ) {
+                                       fprintf( stream, "&nbsp;&gt;&nbsp;" );
+                               }
+                               fprintf( stream, "%s", name );
+                               flag = TRUE;
+                       }
+               }
+               node = g_list_next( node );
+       }
+}
+
+/*
+ * Format an address book folder.
+ * Enter: ctl    Export control data.
+ *        stream Output stream.
+ *        folder Folder.
+ */
+static void exporthtml_fmt_folder(
+               ExportHtmlCtl *ctl, FILE *stream, const ItemFolder *folder )
+{
+       const GList *node;
+       GList *listHeir, *list;
+       const gchar *name;
+       gboolean ret1, ret2;
+
+       name = ADDRITEM_NAME( folder );
+       if( name ) {
+               listHeir = addritem_folder_path( folder, TRUE );
+               if( listHeir ) {
+                       fprintf( stream, "<p class=\"fmt-folder\">" );
+                       fprintf( stream, "%s: ", _( "Folder" ) );
+                       exporthtml_fmt_folderhead( stream, listHeir );
+                       fprintf( stream, "</p>\n" );
+                       g_list_free( listHeir );
+               }
+       }
+
+       ret1 = exporthtml_fmt_person( ctl, stream, folder );
+       ret2 = exporthtml_fmt_group( ctl, stream, folder, ret1 );
+
+       node = list = exporthtml_sort_name( folder->listFolder );
+       while( node ) {
+               AddrItemObject *aio = node->data;
+               if( aio && aio->type == ITEMTYPE_FOLDER ) {
+                       ItemFolder *subFolder = ( ItemFolder * ) aio;
+                       exporthtml_fmt_folder( ctl, stream, subFolder );
+               }
+               node = g_list_next( node );
+       }
+       if( list ) {
+               g_list_free( list );
+       }
+}
+
+/*
+ * Format header block.
+ * Enter:  ctl    Export control data.
+ *         stream Output stream.
+ *         title  Page title.
+ */
+static void exporthtml_fmt_header(
+               ExportHtmlCtl *ctl, FILE *stream, gchar *title )
+{
+       StylesheetEntry *entry;
+
+       entry = exporthtml_find_stylesheet( ctl );
+
+       fprintf( stream,
+               "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\">\n" );
+       fprintf( stream, "<html>\n" );
+       fprintf( stream, "<head>\n" );
+
+       if( ctl->encoding && strlen( ctl->encoding ) > 0 ) {
+               fprintf( stream, "<meta " );
+               fprintf( stream, "http-equiv=\"Content-Type\" " );
+               fprintf( stream, "content=\"text/html; charset=%s\">\n",
+                       ctl->encoding );
+       }
+
+       fprintf( stream, "<title>%s</title>\n", title );
+
+       if( entry != NULL ) {
+               if( entry->fileName && strlen( entry->fileName ) > 0 ) {
+                       fprintf( stream, "<link " );
+                       fprintf( stream, "rel=\"stylesheet\" " );
+                       fprintf( stream, "type=\"text/css\" " );
+                       fprintf( stream, "href=\"%s\" >\n", entry->fileName );
+               }
+       }
+       fprintf( stream, "<body>\n" );
+       fprintf( stream, "</head>\n" );
+}
+
+/*
+ * ============================================================================
+ * Export address book to HTML file.
+ * Enter:  ctl   Export control data.
+ *         cache Address book/data source cache.
+ * Return: Status.
+ * ============================================================================
+ */
+void exporthtml_process(
+       ExportHtmlCtl *ctl, AddressCache *cache )
+{
+       ItemFolder *rootFolder;
+       FILE *htmlFile;
+       time_t tt;
+       gchar *dsName;
+       static gchar *title;
+
+       htmlFile = fopen( ctl->path, "wb" );
+       if( ! htmlFile ) {
+               /* Cannot open file */
+               printf( "Cannot open file for write\n" );
+               ctl->retVal = MGU_OPEN_FILE;
+               return;
+       }
+
+       title = _( "Sylpheed Address Book" );
+       rootFolder = cache->rootFolder;
+       dsName = cache->name;
+
+       exporthtml_fmt_header( ctl, htmlFile, title );
+
+       fprintf( htmlFile, "<body>\n" );
+       fprintf( htmlFile, "<h1>%s</h1>\n", title );
+
+       fprintf( htmlFile, "<p class=\"fmt-folder\">" );
+       fprintf( htmlFile, "%s: ", _( "Address Book" ) );
+       fprintf( htmlFile, "%s", dsName );
+       fprintf( htmlFile, "</p>\n" );
+
+       exporthtml_fmt_folder( ctl, htmlFile, rootFolder );
+
+       tt = time( NULL );
+       fprintf( htmlFile, "<p>%s</p>\n", ctime( &tt ) );
+       fprintf( htmlFile, "<hr width=\"100%\"></hr>\n" );
+
+       fprintf( htmlFile, "</body>\n" );
+       fprintf( htmlFile, "</html>\n" );
+
+       fclose( htmlFile );
+       ctl->retVal = MGU_SUCCESS;
+
+       /* Create stylesheet files */
+       exporthtml_create_css_files( ctl );
+
+}
+
+/*
+ * Build full export file specification.
+ * Enter:  ctl  Export control data.
+ */
+static void *exporthtml_build_filespec( ExportHtmlCtl *ctl ) {
+       gchar *fileSpec;
+
+       fileSpec = g_strconcat(
+               ctl->dirOutput, G_DIR_SEPARATOR_S, ctl->fileHtml, NULL );
+       ctl->path = mgu_replace_string( ctl->path, fileSpec );
+       g_free( fileSpec );
+}
+
+/*
+ * ============================================================================
+ * Parse directory and filename from full export file specification.
+ * Enter:  ctl      Export control data.
+ *         fileSpec File spec.
+ * ============================================================================
+ */
+void exporthtml_parse_filespec( ExportHtmlCtl *ctl, gchar *fileSpec ) {
+       gchar *t;
+
+       ctl->fileHtml =
+               mgu_replace_string( ctl->fileHtml, g_basename( fileSpec ) );
+       t = g_dirname( fileSpec );
+       ctl->dirOutput = mgu_replace_string( ctl->dirOutput, t );
+       g_free( t );
+       ctl->path = mgu_replace_string( ctl->path, fileSpec );
+}
+
+/*
+ * ============================================================================
+ * Test whether directory exists.
+ * Enter:  ctl  Export control data.
+ * Return: TRUE if exists.
+ * ============================================================================
+ */
+gboolean exporthtml_test_dir( ExportHtmlCtl *ctl ) {
+       gboolean retVal;
+       DIR *dp;
+
+       retVal = FALSE;
+       if( dp = opendir( ctl->dirOutput ) ) {
+               retVal = TRUE;
+               closedir( dp );
+       }
+       return retVal;
+}
+
+/*
+ * ============================================================================
+ * Create output directory.
+ * Enter:  ctl  Export control data.
+ * Return: TRUE if directory created.
+ * ============================================================================
+ */
+gboolean exporthtml_create_dir( ExportHtmlCtl *ctl ) {
+       gboolean retVal = FALSE;
+
+       ctl->rcCreate = 0;
+       if( mkdir( ctl->dirOutput, S_IRWXU ) == 0 ) {
+               retVal = TRUE;
+       }
+       else {
+               ctl->rcCreate = errno;
+       }
+       return retVal;
+}
+
+/*
+ * ============================================================================
+ * Retrieve create directory error message.
+ * Enter:  ctl  Export control data.
+ * Return: Message.
+ * ============================================================================
+ */
+gchar *exporthtml_get_create_msg( ExportHtmlCtl *ctl ) {
+       gchar *msg;
+
+       if( ctl->rcCreate == EEXIST ) {
+               msg = _( "Name already exists but is not a directory." );
+       }
+       else if( ctl->rcCreate == EACCES ) {
+               msg = _( "No permissions to create directory." );
+       }
+       else if( ctl->rcCreate == ENAMETOOLONG ) {
+               msg = _( "Name is too long." );
+       }
+       else {
+               msg = _( "Not specified." );
+       }
+       return msg;
+}
+
+/*
+ * Set default values.
+ * Enter: ctl Export control data.
+ */
+static void exporthtml_default_values( ExportHtmlCtl *ctl ) {
+       gchar *str;
+
+       str = g_strconcat(
+               g_get_home_dir(), G_DIR_SEPARATOR_S,
+               DFL_DIR_SYLPHEED_OUT, NULL );
+
+       ctl->dirOutput = mgu_replace_string( ctl->dirOutput, str );
+       g_free( str );
+
+       ctl->fileHtml =
+               mgu_replace_string( ctl->fileHtml, DFL_FILE_SYLPHEED_OUT );
+       ctl->encoding = NULL;
+       ctl->stylesheet = EXPORT_HTML_ID_DEFAULT;
+       ctl->nameFormat = EXPORT_HTML_FIRST_LAST;
+       ctl->banding = TRUE;
+       ctl->linkEMail = TRUE;
+       ctl->showAttribs = TRUE;
+       ctl->retVal = MGU_SUCCESS;
+}
+
+/*
+ * ============================================================================
+ * Load settings from XML properties file.
+ * Enter: ctl  Export control data.
+ * ============================================================================
+ */
+void exporthtml_load_settings( ExportHtmlCtl *ctl ) {
+       XmlProperty *props;
+       gint rc;
+       gchar buf[256];
+
+       *buf = '\0';
+       props = xmlprops_create();
+       xmlprops_set_path( props, ctl->settingsFile );
+       rc = xmlprops_load_file( props );
+       if( rc == 0 ) {
+               /* Read settings */
+               xmlprops_get_property_s( props, EXMLPROP_DIRECTORY, buf );
+               ctl->dirOutput = mgu_replace_string( ctl->dirOutput, buf );
+
+               xmlprops_get_property_s( props, EXMLPROP_FILE, buf );
+               ctl->fileHtml = mgu_replace_string( ctl->fileHtml, buf );
+
+               ctl->stylesheet =
+                       xmlprops_get_property_i( props, EXMLPROP_STYLESHEET );
+               ctl->nameFormat =
+                       xmlprops_get_property_i( props, EXMLPROP_FMT_NAME );
+               ctl->banding =
+                       xmlprops_get_property_b( props, EXMLPROP_BANDING );
+               ctl->linkEMail =
+                       xmlprops_get_property_b( props, EXMLPROP_FMT_EMAIL );
+               ctl->showAttribs =
+                       xmlprops_get_property_b( props, EXMLPROP_FMT_ATTRIB );
+       }
+       else {
+               /* Set default values */
+               exporthtml_default_values( ctl );
+       }
+       exporthtml_build_filespec( ctl );
+       /* exporthtml_print( ctl, stdout ); */
+
+       xmlprops_free( props );
+}
+
+/*
+ * ============================================================================
+ * Save settings to XML properties file.
+ * Enter: ctl  Export control data.
+ * ============================================================================
+ */
+void exporthtml_save_settings( ExportHtmlCtl *ctl ) {
+       XmlProperty *props;
+
+       props = xmlprops_create();
+       xmlprops_set_path( props, ctl->settingsFile );
+
+       xmlprops_set_property( props, EXMLPROP_DIRECTORY, ctl->dirOutput );
+       xmlprops_set_property( props, EXMLPROP_FILE, ctl->fileHtml );
+       xmlprops_set_property_i( props, EXMLPROP_STYLESHEET, ctl->stylesheet );
+       xmlprops_set_property_i( props, EXMLPROP_FMT_NAME, ctl->nameFormat );
+       xmlprops_set_property_b( props, EXMLPROP_BANDING, ctl->banding );
+       xmlprops_set_property_b( props, EXMLPROP_FMT_EMAIL, ctl->linkEMail );
+       xmlprops_set_property_b( props, EXMLPROP_FMT_ATTRIB, ctl->showAttribs );
+       xmlprops_save_file( props );
+       xmlprops_free( props );
+}
+
+/*
+ * ============================================================================
+ * End of Source.
+ * ============================================================================
+ */
+
+
diff --git a/src/exporthtml.h b/src/exporthtml.h
new file mode 100644 (file)
index 0000000..ba1f522
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 2002 Match Grun
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * Definitions for an E-Mail address harvester.
+ */
+
+#ifndef __EXPORT_HTML_H__
+#define __EXPORT_HTML_H__
+
+#include <glib.h>
+
+#include "addrcache.h"
+
+/* Stylesheet ID's */
+#define EXPORT_HTML_ID_NONE     0
+#define EXPORT_HTML_ID_DEFAULT  1
+#define EXPORT_HTML_ID_FULL     2
+#define EXPORT_HTML_ID_CUSTOM   3
+#define EXPORT_HTML_ID_CUSTOM2  4
+#define EXPORT_HTML_ID_CUSTOM3  5
+#define EXPORT_HTML_ID_CUSTOM4  6
+
+#define EXPORT_HTML_FIRST_LAST  0
+#define EXPORT_HTML_LAST_FIRST  1
+
+/* Export HTML control data */
+typedef struct _ExportHtmlCtl ExportHtmlCtl;
+struct _ExportHtmlCtl {
+       gchar    *path;
+       gchar    *dirOutput;
+       gchar    *fileHtml;
+       gchar    *encoding;
+       gchar    *settingsFile;
+       gint     stylesheet;
+       gint     nameFormat;
+       gboolean banding;
+       gboolean linkEMail;
+       gboolean showAttribs;
+       gint     retVal;
+       gint     rcCreate;
+       GList    *listStyle;
+};
+
+/* Function prototypes */
+ExportHtmlCtl *exporthtml_create( void );
+void exporthtml_free           ( ExportHtmlCtl *ctl );
+void exporthtml_set_path       ( ExportHtmlCtl *ctl,
+                                 const gchar *value );
+void exporthtml_set_file_html  ( ExportHtmlCtl *ctl,
+                                 const gchar *value );
+void exporthtml_set_encoding   ( ExportHtmlCtl *ctl,
+                                 const gchar *value );
+void exporthtml_set_stylesheet ( ExportHtmlCtl *ctl,
+                                 const gint value );
+void exporthtml_set_name_format        ( ExportHtmlCtl *ctl,
+                                 const gint value );
+void exporthtml_set_banding    ( ExportHtmlCtl *ctl,
+                                 const gboolean value );
+void exporthtml_set_link_email ( ExportHtmlCtl *ctl,
+                                 const gboolean value );
+void exporthtml_set_attributes ( ExportHtmlCtl *ctl,
+                                 const gboolean value );
+void exporthtml_process                ( ExportHtmlCtl *ctl,
+                                 AddressCache *cache );
+gboolean exporthtml_test_dir   ( ExportHtmlCtl *ctl );
+gboolean exporthtml_create_dir ( ExportHtmlCtl *ctl );
+gchar *exporthtml_get_create_msg( ExportHtmlCtl *ctl );
+
+void exporthtml_parse_filespec ( ExportHtmlCtl *ctl,
+                                 gchar *fileSpec );
+void exporthtml_load_settings  ( ExportHtmlCtl *ctl );
+void exporthtml_save_settings  ( ExportHtmlCtl *ctl );
+
+#endif /* __EXPORT_HTML_H__ */
+
diff --git a/src/xmlprops.c b/src/xmlprops.c
new file mode 100644 (file)
index 0000000..07faa41
--- /dev/null
@@ -0,0 +1,427 @@
+/*
+ * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 2002 Match Grun
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * General functions for saving properties to an XML file.
+ *
+ * The file is structured as follows:
+ *
+ *   <property-list>
+ *     <property name="first-name" value="Axle" >/
+ *     <property name="last-name"  value="Rose" >/
+ *   </property-list>
+ *             
+ * ***********************************************************************
+ */
+
+
+#include <glib.h>
+#include <stdio.h>
+
+#include "prefs.h"
+#include "xml.h"
+#include "mgutils.h"
+#include "xmlprops.h"
+
+/* Element tag names */
+#define XMLS_ELTAG_PROP_LIST     "property-list"
+#define XMLS_ELTAG_PROPERTY      "property"
+
+/* Attribute tag names */
+#define XMLS_ATTAG_NAME          "name"
+#define XMLS_ATTAG_VALUE         "value"
+
+/*
+ * Create new props.
+ */
+XmlProperty *xmlprops_create( void ) {
+       XmlProperty *props;
+
+       props = g_new0( XmlProperty, 1 );
+       props->path = NULL;
+       props->encoding = NULL;
+       props->propertyTable = g_hash_table_new( g_str_hash, g_str_equal );
+       props->retVal = MGU_SUCCESS;
+       return props;
+}
+
+/*
+ * Properties - file path.
+ */
+void xmlprops_set_path( XmlProperty *props, const gchar *value ) {
+       g_return_if_fail( props != NULL );
+       props->path = mgu_replace_string( props->path, value );
+}
+void xmlprops_set_encoding( XmlProperty *props, const gchar *value ) {
+       g_return_if_fail( props != NULL );
+       props->encoding = mgu_replace_string( props->encoding, value );
+}
+
+/*
+ * Free hash table visitor function.
+ */
+static gint xmlprops_free_entry_vis( gpointer key, gpointer value, gpointer data ) {
+       g_free( key );
+       g_free( value );
+       key = NULL;
+       value = NULL;
+       return TRUE;
+}
+
+/*
+ * Clear all properties.
+ * Enter: props Property object.
+ */
+void xmlprops_clear( XmlProperty *props ) {
+       g_return_if_fail( props != NULL );
+       g_hash_table_foreach_remove(
+               props->propertyTable, xmlprops_free_entry_vis, NULL );
+}
+
+/*
+ * Free props.
+ * Enter: props Property object.
+ */
+void xmlprops_free( XmlProperty *props ) {
+       g_return_if_fail( props != NULL );
+
+       /* Clear property table */
+       xmlprops_clear( props );
+       g_hash_table_destroy( props->propertyTable );
+
+       /* Free up internal objects */
+       g_free( props->path );
+       g_free( props->encoding );
+
+       props->path = NULL;
+       props->encoding = NULL;
+       props->propertyTable = NULL;
+       props->retVal = 0;
+
+       g_free( props );
+}
+
+static void xmlprops_write_elem_s( FILE *fp, gint lvl, gchar *name ) {
+       gint i;
+       for( i = 0; i < lvl; i++ ) fputs( "  ", fp );
+       fputs( "<", fp );
+       fputs( name, fp );
+}
+
+static void xmlprops_write_elem_e( FILE *fp, gint lvl, gchar *name ) {
+       gint i;
+       for( i = 0; i < lvl; i++ ) fputs( "  ", fp );
+       fputs( "</", fp );
+       fputs( name, fp );
+       fputs( ">\n", fp );
+}
+
+static void xmlprops_write_attr( FILE *fp, gchar *name, gchar *value ) {
+       fputs( " ", fp );
+       fputs( name, fp );
+       fputs( "=\"", fp );
+       xml_file_put_escape_str( fp, value );
+       fputs( "\"", fp );
+}
+
+static void xmlprops_write_vis( gpointer key, gpointer value, gpointer data ) {
+       FILE *fp = ( FILE * ) data;
+
+       xmlprops_write_elem_s( fp, 1, XMLS_ELTAG_PROPERTY );
+       xmlprops_write_attr( fp, XMLS_ATTAG_NAME, key );
+       xmlprops_write_attr( fp, XMLS_ATTAG_VALUE, value );
+       fputs( " />\n", fp );
+}
+
+static gint xmlprops_write_to( XmlProperty *props, const gchar *fileSpec ) {
+       PrefFile *pfile;
+       FILE *fp;
+
+       props->retVal = MGU_OPEN_FILE;
+       pfile = prefs_write_open( fileSpec );
+       if( pfile ) {
+               fp = pfile->fp;
+               fprintf( fp, "<?xml version=\"1.0\"" );
+               if( props->encoding && *props->encoding ) {
+                       fprintf( fp, " encoding=\"%s\"", props->encoding );
+               }
+               fprintf( fp, " ?>\n" );
+               xmlprops_write_elem_s( fp, 0, XMLS_ELTAG_PROP_LIST );
+               fputs( ">\n", fp );
+
+               /* Output all properties */
+               g_hash_table_foreach( props->propertyTable, xmlprops_write_vis, fp );
+
+               xmlprops_write_elem_e( fp, 0, XMLS_ELTAG_PROP_LIST );
+               props->retVal = MGU_SUCCESS;
+               if( prefs_write_close( pfile ) < 0 ) {
+                       props->retVal = MGU_ERROR_WRITE;
+               }
+       }
+
+       return props->retVal;
+}
+
+/*
+ * Save properties to file.
+ * return: Status code.
+ */
+gint xmlprops_save_file( XmlProperty *props ) {
+       g_return_val_if_fail( props != NULL, -1 );
+
+       props->retVal = MGU_NO_FILE;
+       if( props->path == NULL || *props->path == '\0' ) return props->retVal;
+       xmlprops_write_to( props, props->path );
+       /*
+       if( props->retVal == MGU_SUCCESS ) {
+       }
+       */
+       return props->retVal;
+}
+
+static void xmlprops_print_vis( gpointer key, gpointer value, gpointer data ) {
+       FILE *stream = ( FILE * ) data;
+
+       fprintf( stream, "-\tname/value:\t%s / %s\n", key, value );
+}
+
+void xmlprops_print( XmlProperty *props, FILE *stream ) {
+       fprintf( stream, "Property File: %s\n", props->path );
+       g_hash_table_foreach( props->propertyTable, xmlprops_print_vis, stream );
+       fprintf( stream, "---\n" );
+}
+
+static void xmlprops_save_property(
+               XmlProperty *props, const gchar *name, const gchar *value )
+{
+       gchar *key;
+       gchar *val;
+
+       if( strlen( name ) == 0 ) return;
+       if( strlen( value ) == 0 ) return;
+       if( g_hash_table_lookup( props->propertyTable, name ) ) return;
+       key = g_strdup( name );
+       val = g_strdup( value );
+       g_hash_table_insert( props->propertyTable, key, val );
+}
+
+#define ATTR_BUFSIZE 256
+
+static void xmlprops_read_props( XmlProperty *props, XMLFile *file ) {
+       GList *attr;
+       gchar *name, *value;
+       gchar pName[ ATTR_BUFSIZE ];
+       gchar pValue[ ATTR_BUFSIZE ];
+
+       while( TRUE ) {
+               *pName = '\0';
+               *pValue = '\0';
+               if (! file->level ) break;
+               xml_parse_next_tag( file );
+               xml_get_current_tag( file );
+               if( xml_compare_tag( file, XMLS_ELTAG_PROPERTY ) ) {
+                       attr = xml_get_current_tag_attr( file );
+                       while( attr ) {
+                               name = ( ( XMLAttr * ) attr->data )->name;
+                               value = ( ( XMLAttr * ) attr->data )->value;
+                               if( strcmp( name, XMLS_ATTAG_NAME ) == 0 ) {
+                                       strcpy( pName, value );
+                               }
+                               else if( strcmp( name, XMLS_ATTAG_VALUE ) == 0 ) {
+                                       strcpy( pValue, value );
+                               }
+                               attr = g_list_next( attr );
+                       }
+                       xmlprops_save_property( props, pName, pValue );
+               }
+       }
+}
+
+#undef ATTR_BUFSIZE
+
+/*
+ * Load properties from file.
+ * return: Status code.
+ */
+gint xmlprops_load_file( XmlProperty *props ) {
+       XMLFile *file = NULL;
+
+       g_return_val_if_fail( props != NULL, -1 );
+       props->retVal = MGU_NO_FILE;
+       file = xml_open_file( props->path );
+       if( file == NULL ) {
+               return props->retVal;
+       }
+
+       props->retVal = MGU_BAD_FORMAT;
+       if( xml_get_dtd( file ) == 0 ) {
+               if( xml_parse_next_tag( file ) == 0 ) {
+                       if( xml_compare_tag( file, XMLS_ELTAG_PROP_LIST ) ) {
+                               xmlprops_read_props( props, file );
+                               props->retVal = MGU_SUCCESS;
+                       }
+               }
+       }
+       xml_close_file( file );
+
+       return props->retVal;
+}
+
+/*
+ * Set property.
+ * Enter: props Property object.
+ *        name  Property name.
+ *        value New value to save.
+ */
+void xmlprops_set_property(
+               XmlProperty *props, const gchar *name, const gchar *value )
+{
+       gchar *key;
+       gchar *val;
+
+       g_return_if_fail( props != NULL );
+       if( name == NULL || strlen( name ) == 0 ) return;
+       if( value == NULL || strlen( value ) == 0 ) return;
+       val = g_hash_table_lookup( props->propertyTable, name );
+       if( val == NULL ) {
+               key = g_strdup( name );
+       }
+       else {
+               g_free( val );
+       }
+       val = g_strdup( value );
+       g_hash_table_insert( props->propertyTable, key, val );
+}
+
+/*
+ * Set property to integer value.
+ * Enter: props Property object.
+ *        name  Property name.
+ *        value New value to save.
+ */
+void xmlprops_set_property_i(
+               XmlProperty *props, const gchar *name, const gint value )
+{
+       gchar buf[32];
+
+       g_return_if_fail( props != NULL );
+       sprintf( buf, "%d", value );
+       xmlprops_set_property( props, name, buf );
+}
+
+/*
+ * Set property to boolean value.
+ * Enter: props Property object.
+ *        name  Property name.
+ *        value New value to save.
+ */
+void xmlprops_set_property_b(
+               XmlProperty *props, const gchar *name, const gboolean value )
+{
+       g_return_if_fail( props != NULL );
+       if( value ) {
+               xmlprops_set_property( props, name, "y" );
+       }
+       else {
+               xmlprops_set_property( props, name, "n" );
+       }
+}
+
+/*
+ * Get property.
+ * Enter:  props Property object.
+ *         name  Property name.
+ * Return: value found, or NULL if none. Should be g_free() when done.
+ */
+gchar *xmlprops_get_property( XmlProperty *props, const gchar *name ) {
+       gchar *val, *value;
+
+       value = NULL;
+       g_return_val_if_fail( props != NULL, value );
+       val = g_hash_table_lookup( props->propertyTable, name );
+       if( val ) {
+               value = g_strdup( val );
+       }
+       return value;
+}
+
+/*
+ * Get property into a buffer.
+ * Enter:  props Property object.
+ *         name  Property name.
+ * Return: value found, or NULL if none. Should be g_free() when done.
+ */
+void xmlprops_get_property_s(
+               XmlProperty *props, const gchar *name, gchar *buffer ) {
+       gchar *val;
+
+       g_return_if_fail( props != NULL );
+       if( buffer == NULL ) return;
+       val = g_hash_table_lookup( props->propertyTable, name );
+       if( val ) {
+               strcpy( buffer, val );
+       }
+}
+
+/*
+ * Get property as integer value.
+ * Enter:  props Property object.
+ *         name  Property name.
+ * Return: value found, or zero if not found.
+ */
+gint xmlprops_get_property_i( XmlProperty *props, const gchar *name ) {
+       gchar *val;
+       gchar *endptr;
+       gchar buffer[32];
+       gint value;
+
+       value = 0;
+       g_return_val_if_fail( props != NULL, value );
+       val = g_hash_table_lookup( props->propertyTable, name );
+       if( val ) {
+               endptr = NULL;
+               value = strtol( val, &endptr, 10 );
+       }
+       return value;
+}
+
+/*
+ * Get property as boolean value.
+ * Enter:  props Property object.
+ *         name  Property name.
+ * Return: value found, or FALSE if not found.
+ */
+gboolean xmlprops_get_property_b( XmlProperty *props, const gchar *name ) {
+       gchar *val;
+       gboolean value;
+
+       value = FALSE;
+       g_return_val_if_fail( props != NULL, value );
+       val = g_hash_table_lookup( props->propertyTable, name );
+       if( val ) {
+               value = ( g_strcasecmp( val, "y" ) == 0 );
+       }
+       return value;
+}
+
+/*
+* End of Source.
+*/
+
+
diff --git a/src/xmlprops.h b/src/xmlprops.h
new file mode 100644 (file)
index 0000000..95df526
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 2002 Match Grun
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * General functions for saving properties to an XML file.
+ */
+
+#ifndef __XMLSAVER_H__
+#define __XMLSAVER_H__
+
+#include <stdio.h>
+#include <glib.h>
+
+/* XML property data */
+typedef struct _XmlProperty XmlProperty;
+
+struct _XmlProperty {
+       gchar      *path;
+       gchar      *encoding;
+       GHashTable *propertyTable;
+       gint       retVal;
+};
+
+/* Function prototypes */
+
+XmlProperty *xmlprops_create   ( void );
+void xmlprops_clear            ( XmlProperty *props );
+void xmlprops_free             ( XmlProperty *props );
+void xmlprops_set_path         ( XmlProperty *props, const gchar *value );
+void xmlprops_set_encoding     ( XmlProperty *props, const gchar *value );
+void xmlprops_print            ( XmlProperty *props, FILE *stream );
+gint xmlprops_load_file                ( XmlProperty *props );
+gint xmlprops_save_file                ( XmlProperty *props );
+void xmlprops_set_property     ( XmlProperty *props, const gchar *name,
+                                 const gchar *value );
+void xmlprops_set_property_i   ( XmlProperty *props, const gchar *name,
+                                 const gint value );
+void xmlprops_set_property_b   ( XmlProperty *props, const gchar *name,
+                                 const gboolean value );
+gchar *xmlprops_get_property   ( XmlProperty *props, const gchar *name );
+void xmlprops_get_property_s   ( XmlProperty *props, const gchar *name,
+                                 gchar *buffer );
+gint xmlprops_get_property_i   ( XmlProperty *props, const gchar *name );
+gboolean xmlprops_get_property_b( XmlProperty *props, const gchar *name );
+
+#endif /* __XMLSAVER_H__ */