/* 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 <gdk/gdkkeysyms.h>
#include <strings.h>

#include "common.h"

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

#define RESULT_KEY   "Result"
#define POSITION_KEY "Position"
#define STATUS_KEY   "Message"

#define STATUS_SIZE 64

#define QUERY_NEW    0
#define QUERY_SWITCH 1
#define QUERY_FAKE   2
#define QUERY_ERROR  3

#define IS_SEPARATOR(c) ((c == ' ')  || (c == '.')  || (c == '\r') || \
			 (c == '\n') || (c == '\t') || (c == ',')  || \
			 (c == '(')  || (c == '('))

typedef struct _key_style {
  GdkFont *font;
  GdkColor *fore;
  GdkColor *back;
} KeyStyle;

static GtkWidget *query_notebook;
static GtkWidget *result_notebook;
static guint switch_handler;

static int query_status = 0;
static int nb_queries = 0;
static int last_nb = 0;
static GdkFont *query_font;

static Keyword *last_completion = NULL;
static char *last_start = NULL;
static int last_index = 0;

typedef struct _change_name_dialog {
  GtkWidget *dialog;
  GtkWidget *newname;
} ChangeNameDialog;

int NbQueries() {
  return nb_queries;
}
void InitQueries() {
  last_nb = 0;
}

/* Adds a keyword in the list in alphabetical order */
void AddKeyword(DBConnection *conn, char *key, int type) {
  Keyword *cur, *prev = NULL, *now = conn->keylist;

  cur = (Keyword *) g_malloc(sizeof(Keyword));
  cur->key = g_strdup(key);
  cur->type = type;

  while (now != NULL) {
    int cmp = strcasecmp(cur->key, now->key);
    
    if (cmp < 0) {
      cur->next = now;
      if (prev != NULL)
	prev->next = cur;
      else
	conn->keylist = cur;
      break;
    } else if (cmp == 0) {
      if (cur->type < now->type) {
	cur->next = now;
	if (prev != NULL)
	  prev->next = cur;
	else
	  conn->keylist = cur;
	break;
      } else if (cur->type == now->type) {
	break;
      }
    }
    
    prev = now;
    now = now->next;
  }
  if (now == NULL) {
    cur->next = NULL;
    if (prev != NULL)
      prev->next = cur;
    else
      conn->keylist = cur;
  }
}

void RemoveKeywords(DBConnection *conn, int type) {
  Keyword *prev = NULL, *cur = conn->keylist, *next;

  while (cur != NULL) {
    next = cur->next;

    if (cur->type == type) {
      if (prev == NULL) 
	conn->keylist = cur->next;
      else
	prev->next = cur->next;
      g_free(cur->key);
      g_free(cur);
    } else {
      prev = cur;
    }
    
    cur = next;
  }
}

void RemoveAllKeywords(DBConnection *conn) {
  Keyword *next, *cur = conn->keylist;

  while (cur != NULL) {
    next = cur->next;
    
    g_free(cur->key);
    g_free(cur);

    cur = next;
  }
}

static gint key_press_event(GtkWidget *widget, GdkEventKey *event,
			    gpointer data) {
  GtkWidget *text = GTK_WIDGET(data);

  /*printf("%d %d\n", event->keyval, event->state);*/

  if (event->state == GDK_MOD1_MASK) {
    /* We do not transmit to the Text Widget any of the Alt event */
    gtk_signal_emit_stop_by_name(GTK_OBJECT(data), "key_press_event");

    /* Alt - t = Display table */
    if (event->keyval == 't') {
      gint index, start, end, length;
      
      index = gtk_text_get_point(GTK_TEXT(text));
      length = gtk_text_get_length(GTK_TEXT(text));
      start = index;
      end = index;
      while ((start >= 0) &&
	     (!IS_SEPARATOR(GTK_TEXT_INDEX(GTK_TEXT(text), start))))
	start--;
      start++;
      while ((end < length) &&
	     (!IS_SEPARATOR(GTK_TEXT_INDEX(GTK_TEXT(text), end))))
	end++;
      
      DisplayTableNamed(gtk_editable_get_chars(GTK_EDITABLE(text),
					       start, end));
      
      return (TRUE);
    }
    /* Alt - n = New query */
    if (event->keyval == 'n') {
      AddQuery();
      return (TRUE);
    }
    /* Alt - d = Delete query */
    if (event->keyval == 'd') {
      if (nb_queries > 1) {
	DeleteQuery();
      }
      return (TRUE);
    }
    /* Alr - r = Rename query */
    if (event->keyval == 'r') {
      RenameQuery();
      return (TRUE);
    }
    /* Alt - s = Send Query */
    if (event->keyval == 's') {
      SendQuery();
      return (TRUE);
    }
    /* Alt - e = Export Query */
    if (event->keyval == 'e') {
      ExportQueryDialog();
      return (TRUE);
    }
    
    /* Alt arrows = switch between queries */
    if (event->keyval == GDK_Left) {
      int curpage;
      
      gtk_notebook_prev_page(GTK_NOTEBOOK(query_notebook));

      curpage = gtk_notebook_current_page(GTK_NOTEBOOK(query_notebook));
      gtk_widget_grab_focus(gtk_notebook_get_nth_page(GTK_NOTEBOOK(query_notebook),
						      curpage));

      return (TRUE);
    }
    if (event->keyval == GDK_Right) {
      int curpage;
      
      gtk_notebook_next_page(GTK_NOTEBOOK(query_notebook));
      
      curpage = gtk_notebook_current_page(GTK_NOTEBOOK(query_notebook));
      gtk_widget_grab_focus(gtk_notebook_get_nth_page(GTK_NOTEBOOK(query_notebook),
						      curpage));

      return (TRUE);
    }
  }

  return (FALSE);
}

static gint changed_event(GtkWidget *widget, gpointer data)
{
  GtkWidget *text = GTK_WIDGET(data);
  gint index;

  index = gtk_text_get_point(GTK_TEXT(text));

  /* This is in case of a cursor movement without any changes */
  if (index != last_index + 1) {
    last_completion = NULL;
    g_free(last_start);
    last_start = NULL;
  }

  switch (GTK_TEXT_INDEX(GTK_TEXT(text), index - 1)) {
  case '\t': {     /* Tabulation */
    int lstart_length, linserted_length, word_start;
    Keyword *cur;
    
    gtk_text_freeze(GTK_TEXT(text));

    /* First, we test if it the first time we press on TAB */
    if (last_completion != NULL) {
      /* Not the first time -> we use the same "start letters" as
         before */
      cur = last_completion->next;
      linserted_length = strlen(last_completion->key);
    } else {
      /* We first search for the characters to complete */
      word_start = index - 2;
      while (word_start >= 0) {
	char c = GTK_TEXT_INDEX(GTK_TEXT(text), word_start);
	if (IS_SEPARATOR(c))
	  break;
	word_start--;
      }
      word_start++;
      /* Now, we get the characters and compare them to the keywords */
      last_start =
	gtk_editable_get_chars(GTK_EDITABLE(text), word_start, index - 1);
      linserted_length = -1;

      /* We search the whole list of keywords */
      cur = conn->keylist;
    }
    /* We search for a keyword starting with last_start */
    if ((last_start != NULL) && ((lstart_length = strlen(last_start)) > 0)) {
      if (linserted_length < 0)
	linserted_length = lstart_length;
      
      last_completion = NULL;
      while (cur != NULL) {
	if (!strncasecmp(last_start, cur->key, lstart_length)) {
	  last_completion = cur;
	  break;
	}

	cur = cur->next;
      }
    }

    /* Now we change the text */
    if (linserted_length < 0)
      linserted_length = 0;
    
    gtk_text_set_point(GTK_TEXT(text),
		       index - 1 - linserted_length);
    if (last_completion != NULL) {
      /* We insert the new keyword */
      gtk_text_insert(GTK_TEXT(text), NULL, NULL, NULL,
		      cur->key, -1);
      gtk_text_set_point(GTK_TEXT(text),
			 index + strlen(cur->key));
      last_index = index - 1 - linserted_length + strlen(cur->key);
    } else {
      if (last_start != NULL) {
	/* Or the original letters */
	gdk_beep();
	gtk_text_insert(GTK_TEXT(text), NULL, NULL, NULL,
			last_start, -1);
	gtk_text_set_point(GTK_TEXT(text),
			   index + strlen(last_start));
	last_index = index - 1 - linserted_length + strlen(last_start);
      } else {
	last_index = index - 1 - linserted_length;
      }
    }
    gtk_text_backward_delete(GTK_TEXT(text), linserted_length + 1);
    
    gtk_text_thaw(GTK_TEXT(text));
    break;    
  }

  default:
    last_completion = NULL;
    g_free(last_start);
    last_start = NULL;
    last_index = index;
    break;
  }

  /* Now, syntax coloring */

  return (FALSE);
}

void DisplayResults(void *result, int why, int position) {  
  if (why == QUERY_FAKE) {
    GtkWidget *result_widget = gtk_label_new("No query.");

    gtk_widget_show(result_widget);
    if (position >= 0)
      gtk_notebook_insert_page(GTK_NOTEBOOK(result_notebook),
			       result_widget, NULL, position);
    else
      gtk_notebook_append_page(GTK_NOTEBOOK(result_notebook),
			       result_widget, NULL);
  } else if (why == QUERY_SWITCH) {
   
    gtk_notebook_set_page(GTK_NOTEBOOK(result_notebook), position);
  } else { /* NEW and ERROR */
    GtkWidget *result_widget;
    GtkWidget *result_clist;
    
    if (why == QUERY_ERROR) {
      if (result == NULL)
	result_widget = gtk_label_new("Error in query.");
      else
	result_widget = gtk_label_new((char *) result);
    } else {
      int nb_tuples, nb_cols, i, j;
      int maxwidth;
      char **col_titles;
      GdkFont *font;
      
      status_push("Displaying query results...");
      
      if (conn->DBget_query_status(conn, result) == DB_QUERY_RECORDS_OK) {
	nb_tuples = conn->DBget_record_number(conn, result);
	nb_cols = conn->DBget_field_number(conn, result);
	
	/* Init the CList */
	col_titles = (char **) g_malloc(nb_cols * sizeof(char *));
	for (i = 0; i < nb_cols; i++)
	  col_titles[i] = conn->DBget_field_name(conn, result, i);

	result_widget = gtk_scrolled_window_new(NULL, NULL);
	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(result_widget),
				       GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
	
	result_clist = gtk_clist_new_with_titles(nb_cols, col_titles);
	gtk_widget_show(result_clist);
	gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(result_widget), result_clist);

	font = gtk_widget_get_style(result_clist)->font;
	maxwidth = MAXCOLWID * gdk_char_width(font, 'X');
	for (i = 0; i < nb_cols; i++)
	  gtk_clist_set_column_width(GTK_CLIST(result_clist), i,
				     gdk_string_width(font, col_titles[i]));
	
	/* Fill the CList */
	gtk_clist_freeze(GTK_CLIST(result_clist));
	for (i = 0; i < nb_tuples; i++) {
	  for (j = 0; j < nb_cols; j++) {
	    int width;
	    
	    col_titles[j] = conn->DBget_field_value(conn, result, i, j);
	    width = gdk_string_width(font, col_titles[j]);
	    
	    if (width > GTK_CLIST(result_clist)->column[j].width) {
	      if (width > maxwidth)
		gtk_clist_set_column_width(GTK_CLIST(result_clist),
					   j, maxwidth);
	      else
		gtk_clist_set_column_width(GTK_CLIST(result_clist),
					   j, width);   
	    }
	  }
	  gtk_clist_append(GTK_CLIST(result_clist), col_titles);
	}
	gtk_clist_thaw(GTK_CLIST(result_clist));
	g_free(col_titles);
      } else if (conn->DBget_query_status(conn, result) == DB_QUERY_COMMAND_OK)
	result_widget = gtk_label_new("Command ok.");
      else if (conn->DBget_query_status(conn, result) == DB_QUERY_EMPTY)
	result_widget = gtk_label_new("Empty query.");
      else
	result_widget = gtk_label_new("Error in query.");

      status_pop();
    }
    gtk_widget_show(result_widget);
    gtk_notebook_remove_page(GTK_NOTEBOOK(result_notebook), position);
    gtk_notebook_insert_page(GTK_NOTEBOOK(result_notebook), result_widget,
			     NULL, position);
    gtk_notebook_set_page(GTK_NOTEBOOK(result_notebook), position);
  }
}

GtkWidget *GetCurrentQuery() {
  int cur_page = gtk_notebook_current_page(GTK_NOTEBOOK(query_notebook));
  
  return gtk_notebook_get_nth_page(GTK_NOTEBOOK(query_notebook), cur_page);
}

void *GetCurrentQueryResult() {
  return (void *) gtk_object_get_data(GTK_OBJECT(GetCurrentQuery()),
				      RESULT_KEY);
}

void *GetQueryResult(GtkWidget *query) {
  return (void *) gtk_object_get_data(GTK_OBJECT(query),
				      RESULT_KEY);
}

char *GetCurrentQueryName() {
  GtkWidget *label;
  int position = gtk_notebook_current_page(GTK_NOTEBOOK(query_notebook));
  char *old_name;
  
  label = gtk_notebook_get_tab_label(GTK_NOTEBOOK(query_notebook),
				     gtk_notebook_get_nth_page(GTK_NOTEBOOK(query_notebook),
							       position));
  gtk_label_get(GTK_LABEL(label), &old_name);

  return old_name;
}

void SendQuery() {
  GtkWidget *current_query = GetCurrentQuery();
  char *cquery = gtk_editable_get_chars(GTK_EDITABLE(current_query), 0, -1);
  void *result =
    (void *) gtk_object_get_data(GTK_OBJECT(current_query), RESULT_KEY);
  char *status = (char *) gtk_object_get_data(GTK_OBJECT(current_query),
					      STATUS_KEY);
  
  if (result != NULL)
    conn->DBclear_query(conn, result);

  status_push("Query in progress...");
  
  result = conn->DBexecute_query(conn, cquery);
  gtk_object_set_data(GTK_OBJECT(current_query),
		      RESULT_KEY,
		      result);
  if ((conn->DBget_query_status(conn, result) != DB_QUERY_RECORDS_OK) &&
      (conn->DBget_query_status(conn, result) != DB_QUERY_COMMAND_OK) &&
      (conn->DBget_query_status(conn, result) != DB_QUERY_EMPTY)) {
    GtkWidget *dialog;
    char buf[256];

    sprintf(buf, " Error executing query \n %s ",
	    conn->DBget_error_message(conn));

    dialog = CreateStandardDialog("Error executing query",
				  buf,
				  1, NULL,
				  "Ok", NULL,
				  NULL, NULL,
				  NULL);
    gtk_widget_show(dialog);
    DisplayResults((void *) buf, QUERY_ERROR,
		   gtk_notebook_current_page(GTK_NOTEBOOK(query_notebook)));

    sprintf(status, "Error in query");
  } else {
    int nbf = conn->DBget_field_number(conn, result);
    int nbr = conn->DBget_record_number(conn, result);
    
    DisplayResults(result, QUERY_NEW,
		   gtk_notebook_current_page(GTK_NOTEBOOK(query_notebook)));
    sprintf(status, "Query results : %d field%sand %d record%s",
	    nbf, (nbf != 1 ? "s ": " "),
	    nbr, (nbr != 1 ? "s": ""));
  }

  g_free(cquery);

  status_pop();

  if (status != NULL) {
    if (query_status == 1)
      status_pop();
    status_push(status);
    query_status = 1;
  } else
    query_status = 0; 
}

void AddQuery() {
  GtkStyle *style;
  GtkWidget *label;
  GtkWidget *current_query;
  char buf[64];
  char *status_buffer;
  
  /* The query text zone */
  current_query = gtk_text_new(NULL, NULL);
  gtk_text_set_editable(GTK_TEXT(current_query), TRUE);
  style = gtk_style_copy(gtk_widget_get_style(current_query));
  style->font = query_font;
  gtk_widget_set_style(current_query, style);
  gtk_signal_connect(GTK_OBJECT(current_query), "changed",
		     GTK_SIGNAL_FUNC(changed_event), current_query);
  gtk_signal_connect(GTK_OBJECT(current_query), "key_press_event",
		     GTK_SIGNAL_FUNC(key_press_event), current_query);
  gtk_widget_show(current_query);
  gtk_object_set_data(GTK_OBJECT(current_query),
		      RESULT_KEY,
		      NULL);
  
  /* Add the default query to the notebook */
  sprintf(buf, "Query %d", ++last_nb);
  label = gtk_label_new(buf);
  gtk_widget_show(label);
  gtk_notebook_append_page(GTK_NOTEBOOK(query_notebook),
			   current_query,
			   label);
  gtk_object_set_data(GTK_OBJECT(current_query),
		      POSITION_KEY,
		      (void *) nb_queries);
  
  /* Add an empty result to the result notebook */
  DisplayResults(NULL, QUERY_FAKE, -1);

  /* Add to the query a buffer to display a query status */
  status_buffer = (char *) malloc(STATUS_SIZE * sizeof(char));
  strcpy(status_buffer, "No query executed");
  gtk_object_set_data(GTK_OBJECT(current_query),
		      STATUS_KEY,
		      (void *) status_buffer);

  if (nb_queries == 0) {
    status_push(status_buffer);
    query_status = 1;
  }
  
  nb_queries++;
}

static int DeleteQueryAtPosition(int position) {
  GtkWidget *current_query;
  void *result;
  
  current_query =
    gtk_notebook_get_nth_page(GTK_NOTEBOOK(query_notebook), position);
  result = (void *) gtk_object_get_data(GTK_OBJECT(current_query),
					RESULT_KEY);

  /* Need to free the status buffer... */
  g_free(gtk_object_get_data(GTK_OBJECT(current_query),
			     STATUS_KEY));
  if (result != NULL)
    conn->DBclear_query(conn, result);

  if (position > 0)
    gtk_widget_grab_focus(gtk_notebook_get_nth_page(GTK_NOTEBOOK(query_notebook),
						    position - 1));
  else if (nb_queries > 1)
    gtk_widget_grab_focus(gtk_notebook_get_nth_page(GTK_NOTEBOOK(query_notebook),
						    position + 1));
  
  gtk_notebook_remove_page(GTK_NOTEBOOK(query_notebook), position);
  gtk_notebook_remove_page(GTK_NOTEBOOK(result_notebook), position);
  nb_queries--;

  return nb_queries;
}

int DeleteQuery() {
  gint current_page = gtk_notebook_current_page(GTK_NOTEBOOK(query_notebook));

  if (query_status == 1) {
    query_status = 0;
    status_pop();
  }
  
  return DeleteQueryAtPosition(current_page);
}

static void generic_cancel_callback(GtkWidget *widget, gpointer data) {
  gtk_widget_destroy(GTK_WIDGET(data));
}

static void change_name_ok_callback(GtkWidget *widget, gpointer data) {
  ChangeNameDialog *cd = (ChangeNameDialog *) data;
  char *newname;
  
  newname = gtk_entry_get_text(GTK_ENTRY(cd->newname));
  if ((newname != NULL) && (strlen(newname) == 0))
    newname = NULL;

  if (newname != NULL) {
    int position =  gtk_notebook_current_page(GTK_NOTEBOOK(query_notebook));
    GtkWidget *page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(query_notebook),
						position);
    
    gtk_notebook_set_tab_label_text(GTK_NOTEBOOK(query_notebook), page, newname);
    gtk_notebook_set_menu_label_text(GTK_NOTEBOOK(query_notebook), page, newname);

    gtk_widget_destroy(cd->dialog);
  } else {
    GtkWidget *dialog =
      CreateStandardDialog("Error in query name",
			   "You need to specify a non empty name.",
			   1, NULL,
			   "Ok", NULL,
			   NULL, NULL,
			   NULL);
    gtk_widget_show(dialog);
  }
}

  /* Creates a dialog to change the name of the notebook signet */
void RenameQuery() {
  GtkWidget *dialog;
  GtkWidget *label, *hbox, *name;
  GtkWidget *ok_button, *cancel_button;
  ChangeNameDialog *okdata;
  int position = gtk_notebook_current_page(GTK_NOTEBOOK(query_notebook));
  char *old_name;
  
  /* The connection dialog */
  dialog = gtk_dialog_new();
  gtk_window_set_title(GTK_WINDOW(dialog), "Change name");
  gtk_window_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE);
  gtk_window_set_policy(GTK_WINDOW(dialog), FALSE, FALSE, FALSE);
  gtk_grab_add(dialog);
  
  hbox = gtk_hbox_new(FALSE, 0);
  gtk_container_border_width(GTK_CONTAINER(hbox), 6);
  label = gtk_label_new("New name : ");
  gtk_box_pack_start(GTK_BOX(hbox),
		     label, FALSE, FALSE, 0);
  gtk_widget_show(label);

  name = gtk_entry_new();
  label = gtk_notebook_get_tab_label(GTK_NOTEBOOK(query_notebook),
				     gtk_notebook_get_nth_page(GTK_NOTEBOOK(query_notebook),
							       position));
  gtk_label_get(GTK_LABEL(label), &old_name);
  gtk_entry_set_text(GTK_ENTRY(name), old_name);
  gtk_box_pack_start(GTK_BOX(hbox),
		     name, TRUE, TRUE, 0);
  gtk_widget_show(name);
  
  gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox),
		     hbox, FALSE, FALSE, 0);
  gtk_widget_show(hbox);
  
  /* The buttons */
  okdata = (ChangeNameDialog *) malloc(sizeof(ChangeNameDialog));
  okdata->dialog = dialog;
  okdata->newname = name;

  gtk_signal_connect(GTK_OBJECT(name), "activate",
		     GTK_SIGNAL_FUNC(change_name_ok_callback), okdata);
  
  ok_button = gtk_button_new_with_label("Ok");
  gtk_signal_connect(GTK_OBJECT(ok_button), "clicked",
		     GTK_SIGNAL_FUNC(change_name_ok_callback), okdata);
  gtk_box_pack_start(GTK_BOX(GTK_DIALOG(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(generic_cancel_callback), dialog);
  gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area),
		     cancel_button, FALSE, FALSE, 0);
  gtk_widget_show(cancel_button);

  gtk_widget_grab_focus(name);
  
  gtk_widget_show(dialog);
}

/* used by both load and save */
static void common_cancel_callback(GtkWidget *widget, gpointer data) {
  GtkWidget *fd = (GtkWidget *) data;
  gtk_widget_destroy(GTK_WIDGET(fd));
}

static void save_ok_callback(GtkWidget *widget, gpointer data) {
  char *filename;
  FILE *fp;
  GtkFileSelection *fs = GTK_FILE_SELECTION(data);
  GtkWidget *current_query = GetCurrentQuery();
  char *cquery = gtk_editable_get_chars(GTK_EDITABLE(current_query), 0, -1);

  /* We get the file name */
  filename = gtk_file_selection_get_filename(fs);

  fp = fopen(filename, "wb");
  fprintf(fp, "%s\n", cquery);
  fclose(fp);

  gtk_widget_destroy(GTK_WIDGET(fs));
}

void SaveQueryDialog(void) {
  GtkWidget *file_dialog;
  char buf[256];

  sprintf(buf, "Saving Query '%s'", GetCurrentQueryName());
  file_dialog = gtk_file_selection_new(buf);

  sprintf(buf, "%s.sql", GetCurrentQueryName());
  gtk_file_selection_set_filename(GTK_FILE_SELECTION(file_dialog), buf);

  gtk_signal_connect
    (GTK_OBJECT(GTK_FILE_SELECTION(file_dialog)->cancel_button),
    "clicked", GTK_SIGNAL_FUNC(common_cancel_callback), file_dialog);
  gtk_signal_connect
    (GTK_OBJECT(GTK_FILE_SELECTION(file_dialog)->ok_button),
    "clicked", GTK_SIGNAL_FUNC(save_ok_callback), file_dialog);

  gtk_widget_show(file_dialog);
}

static void load_ok_callback(GtkWidget *widget, gpointer data) {
  char *filename, *raw_filename;
  char buf[256];
  FILE *fp;
  GtkWidget *cq = GetCurrentQuery();
  GtkFileSelection *fs = GTK_FILE_SELECTION(data);

  /* We get the file name */
  filename = gtk_file_selection_get_filename(fs);
  raw_filename = gtk_entry_get_text(GTK_ENTRY(fs->selection_entry));

  gtk_text_freeze(GTK_TEXT(cq));
  fp = fopen(filename, "r");
  while (fgets(buf, 256, fp) != NULL) {
    gtk_text_insert(GTK_TEXT(cq), NULL, NULL, NULL, buf, -1);
  };
  fclose(fp);
  gtk_text_thaw(GTK_TEXT(cq));

  gtk_widget_destroy(GTK_WIDGET(fs));
}

void LoadQueryDialog(void) {
  GtkWidget *file_dialog = gtk_file_selection_new("Loading Query");
  gtk_file_selection_set_filename(GTK_FILE_SELECTION(file_dialog), "*.sql");

  gtk_signal_connect
    (GTK_OBJECT(GTK_FILE_SELECTION(file_dialog)->cancel_button),
    "clicked", GTK_SIGNAL_FUNC(common_cancel_callback), file_dialog);
  gtk_signal_connect
    (GTK_OBJECT(GTK_FILE_SELECTION(file_dialog)->ok_button),
    "clicked", GTK_SIGNAL_FUNC(load_ok_callback), file_dialog);

  gtk_widget_show(file_dialog);
}

static void notebook_selection_callback(GtkWidget *widget, gpointer data) {
  if (data != NULL) {
    GtkWidget *current_query = ((GtkNotebookPage *) data)->child; 
    int position = (int) gtk_object_get_data(GTK_OBJECT(current_query),
					     POSITION_KEY);
    char *status = (char *) gtk_object_get_data(GTK_OBJECT(current_query),
						STATUS_KEY);
    
    DisplayResults(NULL, QUERY_SWITCH, position);

    if (status != NULL) {
      if (query_status == 1)
	status_pop();
      status_push(status);
    query_status = 1;
    } else
      query_status = 0; 
  }
}

void QueriesNewConnection() {
  AddQuery();

  /* Used to prevent bug in query deletions */
  switch_handler =
    gtk_signal_connect(GTK_OBJECT(query_notebook), "switch_page",
		       GTK_SIGNAL_FUNC(notebook_selection_callback), NULL);
}

void QueriesConnectionClosed() {
  int i, nb = nb_queries;

  /* Used to prevent bugs... */
  gtk_signal_disconnect(GTK_OBJECT(query_notebook),
			switch_handler);
  
  for (i = 0; i < nb; i++)
    DeleteQueryAtPosition(0);
  
  nb_queries = 0;
}

GtkWidget *InitQueryPane()
{
  /* The query notebook */
  query_notebook = gtk_notebook_new();
  gtk_notebook_set_tab_pos(GTK_NOTEBOOK(query_notebook), GTK_POS_TOP);
  gtk_notebook_set_scrollable(GTK_NOTEBOOK(query_notebook), TRUE);
  gtk_notebook_popup_enable(GTK_NOTEBOOK(query_notebook));
  gtk_widget_set_usize(query_notebook, 250, 130);
  gtk_widget_show(query_notebook);

  query_font = gdk_font_load("-*-courier-medium-r-normal--12-*-*-*-*-*-*-*");
  
  return query_notebook;
}

GtkWidget *InitResultPane() {
  result_notebook = gtk_notebook_new();
  gtk_notebook_set_show_tabs(GTK_NOTEBOOK(result_notebook), FALSE);
  gtk_notebook_set_show_border(GTK_NOTEBOOK(result_notebook), FALSE); 
  gtk_widget_show(result_notebook);

  return result_notebook;
}
