/* GtkSQL -- an interactive graphical query tool for PostgreSQL
 * Copyright (C) 1998  Lionel ULMER
 *
 * 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.
 */

#include <string.h>

#include "common.h"

#include "queries.h"
#include "dialogs.h"
#include "tables.h"
#include "export.h"
#include "status.h"
#include "mytoolbar.h"

#ifdef USE_POSTGRESQL
#include "postgres.h"
#endif
#ifdef USE_MYSQL
#include "mysql.h"
#endif

/* Icons for the toolbar */
#include "Pics/gtksql.xpm"
#include "Pics/connect.xpm"
#include "Pics/disconnect.xpm"
#include "Pics/send.xpm"
#include "Pics/new.xpm"
#include "Pics/save.xpm"
#include "Pics/open.xpm"
#include "Pics/delete.xpm"
#include "Pics/rename.xpm"
#include "Pics/export.xpm"
#include "Pics/refresh.xpm"

#include "Pics/connect-grey.xpm"
#include "Pics/disconnect-grey.xpm"
#include "Pics/send-grey.xpm"
#include "Pics/new-grey.xpm"
#include "Pics/save-grey.xpm"
#include "Pics/open-grey.xpm"
#include "Pics/delete-grey.xpm"
#include "Pics/rename-grey.xpm"
#include "Pics/export-grey.xpm"
#include "Pics/refresh-grey.xpm"

static register_func register_DB[] = {
#ifdef USE_POSTGRESQL
  register_PostgreSQL,
#endif USE_POSTGRESQL
#ifdef USE_MYSQL
  register_MySQL,
#endif USE_MYSQL
  NULL
};
static GtkWidget *window = NULL;
static DBConnector **connector_table = NULL;

/* Open dialog variables and constants */
#define DIALOG_FIELDS 0
#define DIALOG_LIST   1

static GtkWidget *open_dialog = NULL;
static GtkWidget *open_dialog_notebook;
static GtkWidget *open_dialog_list;
static int open_dialog_status;

/* Defines for submenus type */
#define NORM_BUTTON   0
#define TOGGLE_BUTTON 1

/* Global exported variables */
DBConnection *conn = NULL;

static void destroy (GtkWidget *widget, gpointer data) ;
static void connect_to_base (GtkWidget *widget, gpointer data) ;
static void disconnect (GtkWidget *widget, gpointer data) ;
static void about (GtkWidget *widget, gpointer data) ;
static void do_query (GtkWidget *widget, gpointer data) ;
static void refresh_tables (GtkWidget *widget, gpointer data) ;
static void editor_display (GtkWidget *widget, gpointer data) ;
static void add_query (GtkWidget *widget, gpointer data) ;
static void delete_query (GtkWidget *widget, gpointer data) ;
static void rename_query (GtkWidget *widget, gpointer data) ;
static void save_dialog (GtkWidget *widget, gpointer data) ;
static void load_dialog (GtkWidget *widget, gpointer data) ;
static void export_dialog (GtkWidget *widget, gpointer data) ;

typedef struct _connect_dialog {
  GtkWidget *dialog;
  GtkWidget *basename;
  GtkWidget *basehost;
  GtkWidget *baseport;
} ConnectDialog;

typedef struct _submenu {
  char *label;
  void (*callback)();
  gpointer data;
  GtkWidget **store_to;
  int type;
} SubMenu;

typedef struct _activator {
  GtkWidget **widget;
  int (*activator)();
} Activator;

static int ConnectionOK() ;
static int ConnectionBad() ;
static int QueryExists() ;
static int Export();

GtkWidget *connect_button;
GtkWidget *disconnect_button;
GtkWidget *do_query_button;
GtkWidget *refresh_tables_button;
GtkWidget *add_query_button;
GtkWidget *delete_query_button;
GtkWidget *rename_query_button;
GtkWidget *disconnect_menu_item;
GtkWidget *connect_menu_item;
GtkWidget *export_menu_item;
GtkWidget *send_menu_item;
GtkWidget *rename_menu_item;
GtkWidget *delete_menu_item;
GtkWidget *new_menu_item;
GtkWidget *save_menu_item;
GtkWidget *load_menu_item;
GtkWidget *export_button;
GtkWidget *save_button;
GtkWidget *load_button;
GtkWidget *refresh_tables_menu_item;
GtkWidget *editor_menu_item;

Activator SensWidget[] = {
  { &connect_button, ConnectionBad },
  { &disconnect_button, ConnectionOK },
  { &do_query_button, QueryExists },
  { &refresh_tables_button, ConnectionOK },
  { &add_query_button, ConnectionOK },
  { &delete_query_button, QueryExists }, 
  { &rename_query_button, QueryExists },
  { &export_button, Export },
  { &save_button, QueryExists },
  { &load_button, QueryExists },
  { &disconnect_menu_item, ConnectionOK },
  { &connect_menu_item, ConnectionBad },
  { &export_menu_item, Export },
  { &send_menu_item, QueryExists },
  { &rename_menu_item, QueryExists },
  { &delete_menu_item, QueryExists },
  { &save_menu_item, QueryExists },
  { &load_menu_item, QueryExists },
  { &new_menu_item, ConnectionOK },
  { &refresh_tables_menu_item, ConnectionOK },
  /* { &editor_menu_item, ConnectionOK }, */
  { NULL, NULL}
};

ToolBar toolbardef[] = {
  { "Connect", connect_xpm, connect_grey_xpm,
    "Connect to a new database", connect_to_base, NULL, &connect_button},

  { "Disconnect", disconnect_xpm, disconnect_grey_xpm,
    "Disconnect from the current database", disconnect, NULL, &disconnect_button},

  { "", NULL, NULL, NULL, NULL, NULL},

  { "Send", send_xpm, send_grey_xpm,
    "Send the query to the database", do_query, NULL, &do_query_button},

  { "New", new_xpm, new_grey_xpm,
    "Add a new query", add_query, NULL, &add_query_button},

  { "Delete", delete_xpm, delete_grey_xpm,
    "Suppress current query", delete_query, NULL, &delete_query_button },

  { "Rename", rename_xpm, rename_grey_xpm,
    "Rename the current query", rename_query, NULL, &rename_query_button},

  { "Save", save_xpm, save_grey_xpm,
    "Save the current query", save_dialog, NULL, &save_button},

  { "Load", open_xpm, open_grey_xpm,
    "Load a query into the current query", load_dialog,  NULL, &load_button},

  { "Export", export_xpm, export_grey_xpm,
    "Export the current query results", export_dialog, NULL, &export_button},

  { "", NULL, NULL, NULL, NULL, NULL},

  { "Refresh", refresh_xpm, refresh_grey_xpm,
    "Refresh the table display", refresh_tables, NULL, &refresh_tables_button},

  { NULL, NULL, NULL, NULL, NULL, NULL}
};

SubMenu DatabaseMenu[] = {
  { "Connect...", connect_to_base, NULL, &connect_menu_item, NORM_BUTTON },
  { "Disconnect", disconnect, NULL, &disconnect_menu_item, NORM_BUTTON },
  { "", NULL, NULL, NULL, NORM_BUTTON },
  { "Quit", destroy, NULL, NULL, NORM_BUTTON },
  { NULL, NULL, NULL, NULL, NORM_BUTTON }
};

SubMenu QueryMenu[] = {
  { "Send", do_query, NULL, &send_menu_item, NORM_BUTTON },
  { "Rename", rename_query, NULL, &rename_menu_item, NORM_BUTTON },
  { "Delete", delete_query, NULL, &delete_menu_item, NORM_BUTTON },
  { "New", add_query, NULL, &new_menu_item, NORM_BUTTON },
  { "", NULL, NULL, NULL, NORM_BUTTON },
  { "Save", save_dialog, NULL, &save_menu_item, NORM_BUTTON },
  { "Load", load_dialog, NULL, &load_menu_item, NORM_BUTTON },
  { "", NULL, NULL, NULL, NORM_BUTTON },
  { "Export...", export_dialog, NULL, &export_menu_item, NORM_BUTTON },
  { NULL, NULL, NULL, NULL, NORM_BUTTON }
};

SubMenu TableMenu[] = {
  { "Refresh", refresh_tables, NULL, &refresh_tables_menu_item, NORM_BUTTON },
  /* { "Editor", editor_display, NULL, &editor_menu_item, TOGGLE_BUTTON }, */
  { NULL, NULL, NULL, NULL, NORM_BUTTON }
};

SubMenu Help[] = {
  { "About...", about, NULL, NULL, NORM_BUTTON },
  { NULL, NULL, NULL, NULL, NORM_BUTTON }
};

SubMenu *Menu[] = {
  DatabaseMenu, QueryMenu, TableMenu, Help, NULL
};
char *MenuNames[] = {
  "Database", "Query", "Table", "Help", NULL
};

static int ConnectionOK() {
  if (conn != NULL)
    return 1;
  else
    return 0;
}
static int ConnectionBad() {
  if (open_dialog == NULL)
    return 0;
  
  if (conn == NULL)
    return 1;
  else
    return 0;
}
static int QueryExists() {
  if (conn == NULL)
    return 0;
  if (NbQueries() > 0)
    return 1;
  return 0;
}
static int Export() {
  if (conn == NULL)
    return 0;
  if (NbQueries() > 0)
    return !(ExportInProgress());
  return 0;
}

void UpdateButtonSensitivity() {
  int i;
      
  i = 0;
  while (SensWidget[i].widget != NULL) {
    int act = (SensWidget[i].activator)();
    
    gtk_widget_set_sensitive(*(SensWidget[i].widget), act);
    
    i++;
  }
}

static void display_error(char *title, char *message) {
  GtkWidget *dialog = CreateStandardDialog(title,
					   message,
					   1, NULL,
					   "Ok", NULL,
					   NULL, NULL,
					   NULL);
  gtk_widget_show(dialog);
}

static void disconnect_callback(void *data) {
  if (conn == NULL) {
    display_error("Internal error",
		  "Internal error : trying to disconnect "
		  "with no current connection");
    return;
  }
  TablesConnectionClosed();
  QueriesConnectionClosed();

  /* Resets the queries counter */ 
  InitQueries();
  
  conn->DBdisconnect(conn);
  conn = NULL;

  gtk_window_set_title(GTK_WINDOW(window), "GtkSQL");
  
  UpdateButtonSensitivity();
}

static void destroy(GtkWidget *widget, gpointer data)
{
  if (conn != NULL)
    disconnect_callback(data);
  
  gtk_main_quit ();
}

static void disconnect(GtkWidget *widget, gpointer data)
{
  GtkWidget *dialog;
  char buf[256];

  if (conn == NULL) {
    display_error("Internal error",
		  "Internal error : trying to disconnect "
		  "with no current connection");
    return;
  }
  
  sprintf(buf, " Are you sure you want \n to disconnect from database %s ? ",
	  conn->DBget_db_name(conn));
  
  dialog = CreateStandardDialog("Disconnect", buf,
				2, NULL,
				"Ok", disconnect_callback,
				"Cancel", NULL,
				NULL);
  gtk_widget_show(dialog);
}

static void export_dialog(GtkWidget *widget, gpointer data) {
  ExportQueryDialog();
  UpdateButtonSensitivity();
}

static void save_dialog(GtkWidget *widget, gpointer data) {
  SaveQueryDialog();
}

static void load_dialog(GtkWidget *widget, gpointer data) {
  LoadQueryDialog();
}

static void about(GtkWidget *widget, gpointer data)
{
  GtkWidget *dialog;
  char buf[512];

  sprintf(buf, "\n %s\n (c) 1998 Lionel ULMER \n Version %s \n %s \n\n",
	  APPNAME, VERSION, URL);
#ifdef USE_POSTGRESQL
  strcat(buf, " PostgreSQL driver installed \n");
#endif USE_POSTGRESQL
#ifdef USE_MYSQL
  strcat(buf, " MySQL driver installed \n");
#endif USE_MYSQL

  dialog = CreateStandardDialog("About " APPNAME, buf,
				0, gtksql_xpm,
				"Ok", NULL,
				NULL, NULL,
				NULL);
  gtk_widget_show(dialog);
}

static GtkWidget *CreateMenus() {
  int i, j;
  GtkWidget *item;
  GtkWidget *menu;
  GtkWidget *menubar;
  SubMenu *cmenu;

  menubar = gtk_menu_bar_new();
  
  i = 0;
  while (MenuNames[i] != NULL) {
    /* First we create the menu */
    menu = gtk_menu_new();
    /* Then we add all the items */
    cmenu = Menu[i];
    j = 0;
    while (cmenu[j].label != NULL) {
      if (strlen(cmenu[j].label) == 0)
	item = gtk_menu_item_new();
      else {
	if (cmenu[j].type == TOGGLE_BUTTON) {
	  item = gtk_check_menu_item_new_with_label(cmenu[j].label);
	  gtk_check_menu_item_set_show_toggle(GTK_CHECK_MENU_ITEM(item),
					      TRUE);
	} else
	  item = gtk_menu_item_new_with_label(cmenu[j].label);
      }
      
      gtk_menu_append(GTK_MENU(menu), item);
      if (cmenu[j].callback != NULL)
	gtk_signal_connect_object(GTK_OBJECT(item), "activate",
				  GTK_SIGNAL_FUNC(cmenu[j].callback),
				  cmenu[j].data);
      gtk_widget_show(item);

      if (cmenu[j].store_to != NULL)
	*(cmenu[j].store_to) = item;
      
      j++;
    }
    /* Now we create the menu - item */
    item = gtk_menu_item_new_with_label(MenuNames[i]);
    gtk_widget_show(item);
    gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), menu);
    gtk_menu_bar_append(GTK_MENU_BAR(menubar), item);
    if (MenuNames[i + 1] == NULL)
      gtk_menu_item_right_justify(GTK_MENU_ITEM(item));
    i++;
  }

  return menubar;
}

static void do_query(GtkWidget *widget, gpointer data) {
  SendQuery();
}

static void add_query(GtkWidget *widget, gpointer data) {
  AddQuery();
  UpdateButtonSensitivity();
}

static void delete_query(GtkWidget *widget, gpointer data) {
  int nb_query;
  
  nb_query = DeleteQuery();

  if (nb_query == 0) {
    UpdateButtonSensitivity();
  }
}

static void rename_query(GtkWidget *widget, gpointer data) {
  RenameQuery();
}

static void refresh_tables(GtkWidget *widget, gpointer data) {
  TablesConnectionClosed();
  TablesNewConnection();
}
static void editor_display(GtkWidget *widget, gpointer data) {
  DisplayEditor();
}

static void CreateToolBar(GtkWidget *mainvbox) {
  GtkWidget *toolbar;
  int tbc;
  
  if (conf.toolbar_conf == TOOLBAR_ICONS)
    tbc = GTK_TOOLBAR_ICONS;
  else if (conf.toolbar_conf == TOOLBAR_BOTH)
    tbc = GTK_TOOLBAR_BOTH;
  else if (conf.toolbar_conf == TOOLBAR_TEXT)
    tbc = GTK_TOOLBAR_TEXT;
  else {
    fprintf(stderr, "Internal error : bad configuration value.\n");
    tbc = GTK_TOOLBAR_BOTH;
  }
    
  toolbar = CreateMyToolbar(mainvbox, toolbardef);

  gtk_box_pack_start(GTK_BOX(mainvbox), toolbar, FALSE, FALSE, 5);
  gtk_widget_show(toolbar);
  
  SetMyToolbarConfig(tbc);
  UpdateButtonSensitivity();
}

static void open_cancel_callback(GtkWidget *widget, gpointer data) {
  GtkWidget *dialog = GTK_WIDGET(data);

  if (open_dialog_status == DIALOG_LIST) {
    /* Hide list */
    gtk_notebook_set_page(GTK_NOTEBOOK(open_dialog_notebook),
			  DIALOG_FIELDS);

    gtk_list_clear_items(GTK_LIST(open_dialog_list), 0, -1);
  }
  
  gtk_grab_remove(dialog);
  gtk_widget_hide(dialog);

  open_dialog_status = DIALOG_FIELDS;
}

static void convert_glist(GList *blist) {
  while (blist) {
    GtkWidget *list_item;
    
    list_item = gtk_list_item_new_with_label((char *) blist->data);
    gtk_widget_show(list_item);
    
    g_free(blist->data);
    blist->data = list_item;

    blist = blist->next;
  }
}

static void open_ok_callback(GtkWidget *widget, gpointer data) {
  DBConnector **connector_table = (DBConnector **) data;
  GtkWidget *notebook = GTK_WIDGET(connector_table[0]);
  GList *bases_list;
  int page;
  int return_status;
  
  /* First, we get the page number */
  page = gtk_notebook_current_page(GTK_NOTEBOOK(notebook));
  page++; /* This is because table[0] = notebook */
  
  /* Then, call the corresponding callback function */
  if (connector_table[page] == NULL) {
    display_error("Internal error",
		  "Internal error : open dialog badly created");
    return;
  }
  if (conn != NULL) {
    display_error("Internal error",
		  "Internal error : connection should be closed");
    return;
  }
  
  if (open_dialog_status == DIALOG_FIELDS) {
    return_status =
      connector_table[page]->connect_callback(connector_table[page]->data,
					      &conn,
					      &bases_list,
					      NULL);
  } else {
    /* In that case, we need to get the base number */
    GList *sel;
    GtkLabel *label;
    char *buf;
    
    sel = GTK_LIST(open_dialog_list)->selection;
    label = GTK_LABEL(gtk_container_children(GTK_CONTAINER(sel->data))->data);
    gtk_label_get(label, &buf);
    
    return_status =
      connector_table[page]->connect_callback(connector_table[page]->data,
					      &conn,
					      &bases_list,
					      buf);
  }
    
  if (return_status == CONNECT_OK) {
    if (conn != NULL) {
      char buf[256];
      
      gtk_grab_remove(open_dialog);
      gtk_widget_hide(open_dialog);
	
      TablesNewConnection();
      QueriesNewConnection();
      UpdateButtonSensitivity();
      
      sprintf(buf, "GtkSQL: %s", conn->DBget_db_name(conn));
      gtk_window_set_title(GTK_WINDOW(window), buf);
      
      open_dialog_status = DIALOG_FIELDS;
      gtk_notebook_set_page(GTK_NOTEBOOK(open_dialog_notebook),
			    DIALOG_FIELDS);

    }
  } else if (return_status == CONNECT_LIST) {
    /* Display list */
    open_dialog_status = DIALOG_LIST;
    
    /* Put elements in the list */
    convert_glist(bases_list);
    gtk_list_clear_items(GTK_LIST(open_dialog_list),
			 0, -1);
    gtk_list_append_items(GTK_LIST(open_dialog_list),
			  bases_list);
    
    gtk_notebook_set_page(GTK_NOTEBOOK(open_dialog_notebook),
			  DIALOG_LIST);

    /* I do not understand why I can't free this list here :/
       
       g_list_free(bases_list); */
  }
}

static void connect_to_base(GtkWidget *widget, gpointer data) {
  if (open_dialog != NULL) {
    if (conn == NULL) {
      gtk_window_position(GTK_WINDOW(open_dialog), GTK_WIN_POS_MOUSE);
      gtk_window_set_policy(GTK_WINDOW(open_dialog), FALSE, FALSE, FALSE);
      gtk_grab_add(open_dialog);
      gtk_widget_show(open_dialog);
    } else {
      char buf[256];
      
      sprintf(buf, " You must disconnect first \n from database %s. ",
	      conn->DBget_db_name(conn));
      display_error("Disconnect", buf);
    }
  } else
    display_error("Internal error",
		  "Internal error : open dialog uninitialized");
}

static void CreateOpenDialog() {
  int nb_db;
  
  /* Count the number of databases supported */
  nb_db = 0;
  while (register_DB[nb_db] != NULL)
    nb_db++;

  if (nb_db == 0) {
    display_error("Compilation error",
		  "Compilation error : you do not have any database driver "
		  "installed. \nPlease check your Makefile to see if you "
		  "have any of the USE_XXX defined ");
  } else {
    GtkWidget *ok_button, *cancel_button, *notebook;
    GtkWidget *list_label, *list_vbox;
    int i;
    
    /* Create the result table */
    connector_table = (DBConnector **) malloc((nb_db + 2) *
					      sizeof(DBConnector *));
    
    /* Creates the top-level dialog */
    open_dialog = gtk_dialog_new();
    gtk_window_set_title(GTK_WINDOW(open_dialog), "Database connection");
    
    /* Buttons... */
    ok_button = gtk_button_new_with_label("Ok");
    gtk_signal_connect(GTK_OBJECT(ok_button), "clicked",
		       GTK_SIGNAL_FUNC(open_ok_callback), connector_table);
    gtk_box_pack_start(GTK_BOX(GTK_DIALOG(open_dialog)->action_area),
		       ok_button, FALSE, FALSE, 0);
    GTK_WIDGET_SET_FLAGS(ok_button, GTK_CAN_DEFAULT);
    gtk_widget_grab_default(ok_button);
    gtk_widget_show(ok_button);
    cancel_button = gtk_button_new_with_label("Cancel");
    gtk_signal_connect(GTK_OBJECT(cancel_button), "clicked",
		       GTK_SIGNAL_FUNC(open_cancel_callback), open_dialog);
    gtk_box_pack_start(GTK_BOX(GTK_DIALOG(open_dialog)->action_area),
		       cancel_button, FALSE, FALSE, 0);
    gtk_widget_show(cancel_button);

    /* The List / Fields notebook */
    open_dialog_notebook = gtk_notebook_new();
    gtk_notebook_set_show_tabs(GTK_NOTEBOOK(open_dialog_notebook), FALSE);
    gtk_notebook_set_show_border(GTK_NOTEBOOK(open_dialog_notebook), FALSE); 
    gtk_box_pack_start(GTK_BOX(GTK_DIALOG(open_dialog)->vbox),
		       open_dialog_notebook, FALSE, FALSE, 0);
    gtk_widget_show(open_dialog_notebook);

    /* The fields notebook */
    notebook = gtk_notebook_new();
    gtk_notebook_set_tab_pos(GTK_NOTEBOOK(notebook), GTK_POS_TOP);
    gtk_notebook_append_page(GTK_NOTEBOOK(open_dialog_notebook),
			     notebook, NULL);
    gtk_widget_show(notebook);
    
    /* Add the pages to the dialog and the callback functions */
    connector_table[0] = (DBConnector *) notebook;
    for (i = 0; i < nb_db; i++) {
      GtkWidget *pane, *label;
      
      connector_table[i + 1] = register_DB[i](&pane, &label, display_error,
					      connector_table,
					      open_ok_callback);
      gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
			       pane, label);
    }
    connector_table[i + 1] = NULL;

    /* The list page */
    list_vbox = gtk_vbox_new(FALSE, 0);
    gtk_widget_show(list_vbox);
    
    list_label = gtk_label_new("Please choose a base : ");
    gtk_widget_show(list_label);
    open_dialog_list = gtk_list_new();
    gtk_widget_show(open_dialog_list);

    gtk_box_pack_start(GTK_BOX(list_vbox), list_label,
		       FALSE, FALSE, 0);
    gtk_box_pack_end(GTK_BOX(list_vbox), open_dialog_list,
		     TRUE, TRUE, 0);

    gtk_notebook_append_page(GTK_NOTEBOOK(open_dialog_notebook),
			     list_vbox, NULL);

    gtk_notebook_set_page(GTK_NOTEBOOK(open_dialog_notebook), 0);
    
    open_dialog_status = DIALOG_FIELDS;
    
    UpdateButtonSensitivity();
  }
}

int main(int argc, char *argv[])
{
  GtkWidget *query, *mainvbox, *mainvpane, *menubar, *statusbar;
  GtkWidget *tables, *hpane, *result;
  
  /* Opens the saved configuration */
  GetConfig();
  
  /* Window / GTK initialisation */
  gtk_init(&argc, &argv);
  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_window_set_title(GTK_WINDOW(window), APPNAME);
  gtk_signal_connect(GTK_OBJECT(window), "destroy",
		     GTK_SIGNAL_FUNC(destroy), NULL);
  gtk_widget_set_usize(window, 720, 400);
  
  /* Top level layout */
  mainvbox = gtk_vbox_new(FALSE, 0);
  gtk_container_add(GTK_CONTAINER(window), mainvbox);
  gtk_widget_realize(mainvbox);
  /* The Menu */
  menubar = CreateMenus();
  gtk_box_pack_start(GTK_BOX(mainvbox), menubar, FALSE, FALSE, 0);
  /* The toolbar */
  CreateToolBar(mainvbox);
  /* The VPane */
  mainvpane = gtk_vpaned_new();
  gtk_box_pack_start(GTK_BOX(mainvbox), mainvpane, TRUE, TRUE, 0);
  /* The HPane */
  hpane = gtk_hpaned_new();
  gtk_paned_add1(GTK_PANED(mainvpane), hpane);
  /* The result */
  result = InitResultPane();
  gtk_paned_add2(GTK_PANED(mainvpane), result);
  /* The Query text zone */
  query = InitQueryPane();
  gtk_paned_add1(GTK_PANED(hpane), query);
  /* The Tables browser */
  tables = InitTablesPane();
  gtk_paned_add2(GTK_PANED(hpane), tables);
  /* The status bar */
  statusbar = status_bar_new();
  status_push("Welcome to " APPNAME " version " VERSION);
  status_reset();
  
  gtk_box_pack_start(GTK_BOX(mainvbox), statusbar, FALSE, FALSE, 0);
  gtk_widget_show(statusbar);
  gtk_widget_show(hpane);
  gtk_widget_show(mainvpane);
  gtk_widget_show(menubar);
  gtk_widget_show(mainvbox);
  gtk_widget_show(window);
  
  /* Various other initialisations */
  UpdateButtonSensitivity();

  /* Creates the "open database" dialog */
  CreateOpenDialog();

  /* Main loop */
  gtk_main();
  
  return 0;
}
