/*
 * $Id: main.c,v 1.4 2003/04/29 03:09:45 nlevitt Exp $
 *
 * Copyright (c) 2003 Noah Levitt
 * 
 * This program is free software; the author gives unlimited permission to
 * copy and/or distribute it, with or without modifications, as long as
 * this notice is preserved.
 * 
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#include <gtk/gtk.h>
#include <fontconfig/fontconfig.h>
#include <waterfall.h>


static GtkWidget *page_label = NULL;
static GtkWidget *style_combo = NULL;
static GtkWidget *autohint_check_button = NULL;
static GtkWidget *rgba_hbox = NULL;


static void
set_style_choices (Waterfall *waterfall)
{
  FcObjectSet *set;
  FcPattern *pattern;
  FcFontSet *list;
  GList *styles = NULL;
  FcChar8 *style;
  gint i;

  if (style_combo == NULL)
    return;

  set = FcObjectSetBuild (FC_STYLE, 0);
  pattern = FcPatternBuild (0, FC_FAMILY, FcTypeString, 
                            waterfall_get_font_family (waterfall), 0);
  g_assert (pattern != NULL);

  list = FcFontList (0, pattern, set);
  FcPatternDestroy (pattern);

  for (i = 0;  i < list->nfont;  i++)
    {
      FcPatternGetString (list->fonts[i], FC_STYLE, 0, &style);
      styles = g_list_append (styles, style);
    }

  if (styles)
    gtk_combo_set_popdown_strings (GTK_COMBO (style_combo), styles);
  FcFontSetDestroy (list);
}


static void
family_changed (GtkEntry *entry,
                Waterfall *waterfall)
{
  const gchar *new_family = gtk_entry_get_text (entry);

  /* ignore empty string (it sends these a lot) */
  if (new_family[0] == '\0')
    return;

  waterfall_set_font_family (waterfall, new_family);
  set_style_choices (waterfall);
}


static void
style_changed (GtkEntry *entry,
               Waterfall *waterfall)
{
  const gchar *new_style = gtk_entry_get_text (entry);

  /* ignore empty string (it sends these a lot) */
  if (new_style[0] == '\0')
    return;

  waterfall_set_font_style (waterfall, new_style);
}


static GtkWidget *
construct_font_family_chooser (Waterfall *waterfall)
{
  GtkWidget *combo;
  FcFontSet *list;
  FcObjectSet *set = FcObjectSetBuild (FC_FAMILY, 0);
  FcPattern *pattern = FcPatternCreate ();
  GList *family_names = NULL;
  FcChar8 *family_name;
  int i;

  combo = gtk_combo_new ();
  gtk_widget_show (combo);
  gtk_editable_set_editable (GTK_EDITABLE (GTK_COMBO (combo)->entry), FALSE);

  g_signal_connect (GTK_COMBO (combo)->entry, "changed",
                    G_CALLBACK (family_changed), waterfall);

  list = FcFontList (0, pattern, set);
  FcObjectSetDestroy (set);
  FcPatternDestroy (pattern);

  g_assert (list->nfont > 0);
  for (i = 0;  i < list->nfont;  i++)
    {
      FcPatternGetString (list->fonts[i], FC_FAMILY, 0, &family_name);
      family_names = g_list_append (family_names, family_name);
    }
  /*
  family_names = g_list_append (family_names, "Serif");
  family_names = g_list_append (family_names, "Sans");
  family_names = g_list_append (family_names, "Monospace");
  */

  family_names = g_list_sort (family_names, (GCompareFunc) FcStrCmpIgnoreCase);
  gtk_combo_set_popdown_strings (GTK_COMBO (combo), family_names);

  g_list_free (family_names);
  FcFontSetDestroy (list);

  return combo;
}


static GtkWidget *
construct_font_style_chooser (Waterfall *waterfall)
{
  style_combo = gtk_combo_new ();
  gtk_widget_show (style_combo);
  gtk_editable_set_editable (GTK_EDITABLE (GTK_COMBO (style_combo)->entry), 
                             FALSE);

  g_signal_connect (GTK_COMBO (style_combo)->entry, "changed",
                    G_CALLBACK (style_changed), waterfall);

  set_style_choices (waterfall);

  return style_combo;
}


static void
hint_toggled (GtkToggleButton *toggle_button,
              Waterfall *waterfall)
{
  waterfall_set_hint (waterfall, 
                      gtk_toggle_button_get_active (toggle_button));

  gtk_widget_set_sensitive (autohint_check_button, 
                            gtk_toggle_button_get_active (toggle_button));
}


static void
autohint_toggled (GtkToggleButton *toggle_button,
              Waterfall *waterfall)
{
  waterfall_set_autohint (waterfall, 
                          gtk_toggle_button_get_active (toggle_button));
}


static void
antialias_toggled (GtkToggleButton *toggle_button,
                   Waterfall *waterfall)
{
  waterfall_set_antialias (waterfall, 
                           gtk_toggle_button_get_active (toggle_button));
  gtk_widget_set_sensitive (rgba_hbox,
                            waterfall_get_antialias (waterfall));
}


static void
min_size_changed (GtkAdjustment *adjustment,
                  Waterfall *waterfall)
{
  waterfall_set_min_size (waterfall, gtk_adjustment_get_value (adjustment));
}


static void
max_size_changed (GtkAdjustment *adjustment,
                  Waterfall *waterfall)
{
  waterfall_set_max_size (waterfall, gtk_adjustment_get_value (adjustment));
}


static void
increment_changed (GtkAdjustment *adjustment,
                   Waterfall *waterfall)
{
  waterfall_set_increment (waterfall, gtk_adjustment_get_value (adjustment));
}


static void
aspect_changed (GtkAdjustment *adjustment,
                Waterfall *waterfall)
{
  waterfall_set_aspect (waterfall, 
                        gtk_adjustment_get_value (adjustment));
}


static GtkWidget *
construct_size_bar (Waterfall *waterfall)
{
  GtkWidget *hbox;
  GtkObject *adjustment;
  GtkWidget *spin_button;
  GtkWidget *label;

  hbox = gtk_hbox_new (FALSE, 12);
  gtk_widget_show (hbox);

  label = gtk_label_new ("Smallest:");
  gtk_widget_show (label);
  gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);

  adjustment = gtk_adjustment_new (waterfall_get_min_size (waterfall),
                                   1.0, 1000.0, 2.0, 12.0, 0);
  g_signal_connect (adjustment, "value-changed", 
                    G_CALLBACK (min_size_changed), waterfall);
  spin_button = gtk_spin_button_new (GTK_ADJUSTMENT (adjustment), 0, 1);
  gtk_widget_show (spin_button);
  gtk_box_pack_start (GTK_BOX (hbox), spin_button, FALSE, FALSE, 0);

  label = gtk_label_new ("Biggest:");
  gtk_widget_show (label);
  gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);

  adjustment = gtk_adjustment_new (waterfall_get_max_size (waterfall),
                                   1.0, 1000.0, 2.0, 12.0, 0);
  g_signal_connect (adjustment, "value-changed", 
                    G_CALLBACK (max_size_changed), waterfall);
  spin_button = gtk_spin_button_new (GTK_ADJUSTMENT (adjustment), 0, 1);
  gtk_widget_show (spin_button);
  gtk_box_pack_start (GTK_BOX (hbox), spin_button, FALSE, FALSE, 0);

  label = gtk_label_new ("Increment:");
  gtk_widget_show (label);
  gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);

  adjustment = gtk_adjustment_new (waterfall_get_increment (waterfall),
                                   0.1, 100.0, 0.1, 1.0, 0);
  g_signal_connect (adjustment, "value-changed", 
                    G_CALLBACK (increment_changed), waterfall);
  spin_button = gtk_spin_button_new (GTK_ADJUSTMENT (adjustment), 0, 1);
  gtk_widget_show (spin_button);
  gtk_box_pack_start (GTK_BOX (hbox), spin_button, FALSE, FALSE, 0);

  return hbox;
}


static GtkWidget *
construct_aspect_bar (Waterfall *waterfall)
{
  GtkWidget *hbox;
  GtkObject *adjustment;
  GtkWidget *spin_button;
  GtkWidget *label;

  hbox = gtk_hbox_new (FALSE, 6);
  gtk_widget_show (hbox);

  label = gtk_label_new ("Aspect:");
  gtk_widget_show (label);
  gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);

  adjustment = gtk_adjustment_new (waterfall_get_aspect (waterfall),
                                   0.01, 100.0, 0.05, 0.5, 0);
  g_signal_connect (adjustment, "value-changed",
                    G_CALLBACK (aspect_changed), waterfall);
  spin_button = gtk_spin_button_new (GTK_ADJUSTMENT (adjustment), 0, 1);
  gtk_widget_show (spin_button);
  gtk_box_pack_start (GTK_BOX (hbox), spin_button, FALSE, FALSE, 0);

  return hbox;
}


static void
set_page_label (Waterfall *waterfall)
{
  gchar *label;
  gint page = waterfall_get_page (waterfall);

  if (page == -1)
    label = g_strdup ("Quick Brown"); /* strdup just so it can be freed */
  else
    label = g_strdup_printf ("U+%4.4X", page * WATERFALL_PAGE_SIZE);

  gtk_label_set_text (GTK_LABEL (page_label), label);

  g_free (label);
}


/* direction should be +1 or -1 */
static void
set_page (Waterfall *waterfall, 
          gint page,
          gint direction)
{
  while (waterfall_is_page_empty (waterfall, page)
         && page + direction <= WATERFALL_LAST_PAGE
         && page + direction >= -1)
    page += direction;

  waterfall_set_page (waterfall, page);
  set_page_label (waterfall);
}


static void
back_back_clicked (GtkButton *button, 
                   Waterfall *waterfall)
{
  set_page (waterfall, waterfall_get_page (waterfall) - 16, -1);
}


static void
back_clicked (GtkButton *button, 
              Waterfall *waterfall)
{
  set_page (waterfall, waterfall_get_page (waterfall) - 1, -1);
}


static void
forward_clicked (GtkButton *button, 
                 Waterfall *waterfall)
{
  set_page (waterfall, waterfall_get_page (waterfall) + 1, 1);
}


static void
forward_forward_clicked (GtkButton *button, 
                         Waterfall *waterfall)
{
  set_page (waterfall, waterfall_get_page (waterfall) + 16, 1);
}


static void
forward_forward_forward_clicked (GtkButton *button, 
                                 Waterfall *waterfall)
{
  set_page (waterfall, WATERFALL_LAST_PAGE, 1);
}


static void
back_back_back_clicked (GtkButton *button, 
                        Waterfall *waterfall)
{
  set_page (waterfall, 0, -1);
}


static void
quick_brown_clicked (GtkButton *button, 
                     Waterfall *waterfall)
{
  set_page (waterfall, -1, -1);
}


static GtkWidget *
construct_page_navigator (Waterfall *waterfall)
{
  GtkWidget *hbox;
  GtkWidget *button;

  hbox = gtk_hbox_new (FALSE, 3);
  gtk_widget_show (hbox);

  button = gtk_button_new_with_label (" <<< ");
  gtk_widget_show (button);
  gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
  g_signal_connect (G_OBJECT (button), "clicked", 
                    G_CALLBACK (back_back_back_clicked), waterfall);

  button = gtk_button_new_with_label ("  <<  ");
  gtk_widget_show (button);
  gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
  g_signal_connect (G_OBJECT (button), "clicked", 
                    G_CALLBACK (back_back_clicked), waterfall);

  button = gtk_button_new_with_label ("  <  ");
  gtk_widget_show (button);
  gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
  g_signal_connect (G_OBJECT (button), "clicked", 
                    G_CALLBACK (back_clicked), waterfall);

  page_label = gtk_label_new (NULL);
  gtk_widget_show (page_label);
  gtk_label_set_selectable (GTK_LABEL (page_label), TRUE);
  gtk_box_pack_start (GTK_BOX (hbox), page_label, FALSE, FALSE, 0);
  set_page_label (waterfall);

  button = gtk_button_new_with_label ("  >  ");
  gtk_widget_show (button);
  gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
  g_signal_connect (G_OBJECT (button), "clicked", 
                    G_CALLBACK (forward_clicked), waterfall);

  button = gtk_button_new_with_label ("  >>  ");
  gtk_widget_show (button);
  gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
  g_signal_connect (G_OBJECT (button), "clicked", 
                    G_CALLBACK (forward_forward_clicked), waterfall);

  button = gtk_button_new_with_label (" >>> ");
  gtk_widget_show (button);
  gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
  g_signal_connect (G_OBJECT (button), "clicked", 
                    G_CALLBACK (forward_forward_forward_clicked), waterfall);

  button = gtk_button_new_with_label ("Quick Brown");
  gtk_widget_show (button);
  gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
  g_signal_connect (G_OBJECT (button), "clicked", 
                    G_CALLBACK (quick_brown_clicked), waterfall);

  return hbox;
}


static void
set_rgba_none (GtkMenuItem *menu_item,
               Waterfall *waterfall)
{
  waterfall_set_rgba (waterfall, FC_RGBA_NONE);
}


static void
set_rgba_rgb (GtkMenuItem *menu_item,
              Waterfall *waterfall)
{
  waterfall_set_rgba (waterfall, FC_RGBA_RGB);
}


static void
set_rgba_bgr (GtkMenuItem *menu_item,
              Waterfall *waterfall)
{
  waterfall_set_rgba (waterfall, FC_RGBA_BGR);
}


static void
set_rgba_vrgb (GtkMenuItem *menu_item,
               Waterfall *waterfall)
{
  waterfall_set_rgba (waterfall, FC_RGBA_VRGB);
}


static void
set_rgba_vbgr (GtkMenuItem *menu_item,
               Waterfall *waterfall)
{
  waterfall_set_rgba (waterfall, FC_RGBA_VBGR);
}


static GtkWidget *
construct_rgba_menu (Waterfall *waterfall)
{
  GtkWidget *option_menu;
  GtkWidget *menu;
  GtkWidget *menu_item;
  GtkWidget *label;

  rgba_hbox = gtk_hbox_new (FALSE, 6);
  gtk_widget_show (rgba_hbox);

  label = gtk_label_new ("Subpixel smoothing:");
  gtk_widget_show (label);
  gtk_box_pack_start (GTK_BOX (rgba_hbox), label, FALSE, FALSE, 0);

  menu = gtk_menu_new ();
  gtk_widget_show (menu);

  menu_item = gtk_menu_item_new_with_label ("None");
  gtk_widget_show (menu_item);
  gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
  g_signal_connect (menu_item, "activate", 
                    G_CALLBACK (set_rgba_none), waterfall);
  
  menu_item = gtk_menu_item_new_with_label ("RGB");
  gtk_widget_show (menu_item);
  gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
  g_signal_connect (menu_item, "activate", 
                    G_CALLBACK (set_rgba_rgb), waterfall);
  
  menu_item = gtk_menu_item_new_with_label ("BGR");
  gtk_widget_show (menu_item);
  gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
  g_signal_connect (menu_item, "activate", 
                    G_CALLBACK (set_rgba_bgr), waterfall);
  
  menu_item = gtk_menu_item_new_with_label ("VRGB");
  gtk_widget_show (menu_item);
  gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
  g_signal_connect (menu_item, "activate", 
                    G_CALLBACK (set_rgba_vrgb), waterfall);
  
  menu_item = gtk_menu_item_new_with_label ("VBGR");
  gtk_widget_show (menu_item);
  gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
  g_signal_connect (menu_item, "activate", 
                    G_CALLBACK (set_rgba_vbgr), waterfall);
  
  option_menu = gtk_option_menu_new ();
  gtk_widget_show (option_menu);
  gtk_box_pack_start (GTK_BOX (rgba_hbox), option_menu, FALSE, FALSE, 0);
  gtk_option_menu_set_menu (GTK_OPTION_MENU (option_menu), menu);

  gtk_widget_set_sensitive (rgba_hbox,
                            waterfall_get_antialias (waterfall));

  return rgba_hbox;
}


static GtkWidget *
construct_options_bar (Waterfall *waterfall)
{
  GtkWidget *vbox;
  GtkWidget *hbox;
  GtkWidget *check_button;
  GtkWidget *separator;

  vbox = gtk_vbox_new (FALSE, 6);
  gtk_widget_show (vbox);

  hbox = gtk_hbox_new (FALSE, 12);
  gtk_widget_show (hbox);

  gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);

  gtk_box_pack_start (GTK_BOX (hbox), 
                      construct_font_family_chooser (waterfall), 
                      FALSE, FALSE, 0);

  gtk_box_pack_start (GTK_BOX (hbox), 
                      construct_font_style_chooser (waterfall), 
                      FALSE, FALSE, 0);

  separator = gtk_vseparator_new ();
  gtk_widget_show (separator);
  gtk_box_pack_start (GTK_BOX (hbox), separator, FALSE, FALSE, 0);

  check_button = gtk_check_button_new_with_label ("Hinting");
  gtk_widget_show (check_button);
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check_button),
                                waterfall_get_hint (waterfall));
  gtk_box_pack_start (GTK_BOX (hbox), check_button, FALSE, FALSE, 0);
  g_signal_connect (G_OBJECT (check_button), "toggled",
                    G_CALLBACK (hint_toggled), waterfall);

  autohint_check_button = gtk_check_button_new_with_label ("Autohinting");
  gtk_widget_show (autohint_check_button);
  gtk_widget_set_sensitive (autohint_check_button, 
                            waterfall_get_hint (waterfall));
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (autohint_check_button),
                                waterfall_get_autohint (waterfall));
  gtk_box_pack_start (GTK_BOX (hbox), autohint_check_button, FALSE, FALSE, 0);
  g_signal_connect (G_OBJECT (autohint_check_button), "toggled",
                    G_CALLBACK (autohint_toggled), waterfall);

  separator = gtk_vseparator_new ();
  gtk_widget_show (separator);
  gtk_box_pack_start (GTK_BOX (hbox), separator, FALSE, FALSE, 0);

  check_button = gtk_check_button_new_with_label ("Anti-Aliasing");
  gtk_widget_show (check_button);
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check_button),
                                waterfall_get_antialias (waterfall));
  gtk_box_pack_start (GTK_BOX (hbox), check_button, FALSE, FALSE, 0);
  g_signal_connect (G_OBJECT (check_button), "toggled",
                    G_CALLBACK (antialias_toggled), waterfall);

  gtk_box_pack_start (GTK_BOX (hbox), construct_rgba_menu (waterfall), 
                      FALSE, FALSE, 0);

  separator = gtk_vseparator_new ();
  gtk_widget_show (separator);
  gtk_box_pack_start (GTK_BOX (hbox), separator, FALSE, FALSE, 0);

  gtk_box_pack_start (GTK_BOX (hbox), construct_aspect_bar (waterfall), 
                      FALSE, FALSE, 0);

  hbox = gtk_hbox_new (FALSE, 12);
  gtk_widget_show (hbox);
  gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);

  gtk_box_pack_start (GTK_BOX (hbox), construct_size_bar (waterfall), 
                      FALSE, FALSE, 0);

  separator = gtk_vseparator_new ();
  gtk_widget_show (separator);
  gtk_box_pack_start (GTK_BOX (hbox), separator, FALSE, FALSE, 0);

  gtk_box_pack_start (GTK_BOX (hbox), construct_page_navigator (waterfall), 
                      FALSE, FALSE, 0);

  return vbox;
}


gint
main (gint argc, gchar **argv)
{
  GtkWidget *window;
  GtkWidget *vbox;
  GtkWidget *waterfall;
  GtkWidget *scrolled_window;
  GdkScreen *screen;

  gtk_init (&argc, &argv);

  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_window_set_title (GTK_WINDOW (window), "Waterfall");
  g_signal_connect (G_OBJECT (window), "destroy",
                    G_CALLBACK (gtk_main_quit), NULL);

  screen = gtk_window_get_screen (GTK_WINDOW (window));
  gtk_window_set_default_size (GTK_WINDOW (window),
                               gdk_screen_get_width (screen) * 3/4,
                               gdk_screen_get_height (screen) * 3/4);

  vbox = gtk_vbox_new (FALSE, 6);
  gtk_widget_show (vbox);
  gtk_container_add (GTK_CONTAINER (window), vbox);

  waterfall = waterfall_new ();
  gtk_widget_show (waterfall);

  scrolled_window = gtk_scrolled_window_new (NULL, NULL);
  gtk_widget_show (scrolled_window);
  gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scrolled_window),
                                         waterfall);

  gtk_box_pack_start (GTK_BOX (vbox), 
                      construct_options_bar (WATERFALL (waterfall)), 
                      FALSE, FALSE, 0);

  gtk_box_pack_start (GTK_BOX (vbox), scrolled_window, 
                      TRUE, TRUE, 0);

  gtk_widget_show (window);

  gtk_main ();

  return 0;
}


