/*
 * This file is part of YAD.
 *
 * YAD 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 3 of the License, or
 * (at your option) any later version.
 *
 * YAD 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 YAD. If not, see <http://www.gnu.org/licenses/>.
 *
 * Copyright (C) 2008-2023, Victor Ananjevsky <victor@sanana.kiev.ua>
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <locale.h>
#include <stdlib.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>

#ifndef G_OS_WIN32
# include <sys/shm.h>
# include <gdk/gdkx.h>
#endif

#include "yad.h"

YadOptions options;
GtkIconTheme *yad_icon_theme;

#ifndef STANDALONE
GSettings *settings;
GSettings *sv_settings;
#endif

GdkPixbuf *big_fallback_image = NULL;
GdkPixbuf *small_fallback_image = NULL;

/* global flag for temporary disable ESC handling (needed for searchbar) */
gboolean ignore_esc = FALSE;

static GtkWidget *dialog = NULL;
static GtkWidget *text = NULL;

static gint ret = YAD_RESPONSE_ESC;

static gboolean is_x11 = FALSE;

YadNTabs *tabs;

#ifndef G_OS_WIN32
static void
sa_usr1 (gint sig)
{
  if (options.plug != -1)
    yad_print_result ();
  else
    yad_exit (options.data.def_resp);
}

static void
sa_usr2 (gint sig)
{
  if (options.plug != -1)
    gtk_main_quit ();
  else
    yad_exit (YAD_RESPONSE_CANCEL);
}
#endif

static gboolean
keys_cb (GtkWidget *w, GdkEventKey *ev, gpointer d)
{
  if (options.plug != -1)
    return FALSE;

  switch (ev->keyval)
    {
    case GDK_KEY_F1:
      if (options.data.f1_action)
        {
          run_command_async (options.data.f1_action);
          return TRUE;
        }
      break;
    case GDK_KEY_Escape:
      if (!ignore_esc)
        {
          if (options.data.escape_ok)
            yad_exit (options.data.def_resp);
          else if (!options.data.no_escape)
            yad_exit (YAD_RESPONSE_ESC);
          return TRUE;
        }
      break;
    case GDK_KEY_Return:
    case GDK_KEY_KP_Enter:
      if (ev->state & GDK_CONTROL_MASK)
        {
          yad_exit (options.data.def_resp);
          return TRUE;
        }
      break;
     }

   return FALSE;
}

static void
btn_cb (GtkWidget *b, gchar *cmd)
{
  if (cmd)
    run_command_async (cmd);
  else
    {
      gint resp = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (b), "resp"));
      yad_exit (resp);
    }
}

static gboolean
timeout_cb (gpointer data)
{
  static guint count = 1;
  GtkWidget *w = (GtkWidget *) data;

  if (options.data.timeout < count)
    {
      yad_exit (YAD_RESPONSE_TIMEOUT);
      return FALSE;
    }

  if (w)
    {
      gdouble percent = ((gdouble) options.data.timeout - count) / (gdouble) options.data.timeout;
      gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (w), percent);
#ifndef STANDALONE
      if (g_settings_get_boolean (settings, "show-remain"))
#else
      if (SHOW_REMAIN)
#endif
        {
          gchar *lbl = g_strdup_printf (_("%d sec"), options.data.timeout - count);
          gtk_progress_bar_set_text (GTK_PROGRESS_BAR (w), lbl);
          g_free (lbl);
        }
    }

  count++;

  return TRUE;
}

static gboolean
unfocus_cb (GtkWidget *w, GdkEventFocus *ev, gpointer d)
{
  if (options.data.close_on_unfocus)
    gtk_main_quit ();
  return FALSE;
}

static gboolean
link_cb (GtkLabel *l, gchar *uri, gpointer d)
{
  open_uri (uri);
  return TRUE;
}

void
yad_exit (gint id)
{
  if ((options.mode == YAD_MODE_FILE) && !(id & 1))
    {
      /* show custom confirmation dialog */
      if (!file_confirm_overwrite (dialog))
        return;
    }

  ret = id;
  gtk_main_quit ();
}

static GtkWidget *
create_layout (GtkWidget *dlg)
{
  GtkWidget *image, *mw, *imw, *layout, *exp, *box;

  layout = image = mw = exp = NULL;

  /* create image */
  if (options.data.dialog_image)
    {
      GdkPixbuf *pb = NULL;

      pb = get_pixbuf (options.data.dialog_image, YAD_BIG_ICON, FALSE);
      image = gtk_image_new_from_pixbuf (pb);
      if (pb)
        g_object_unref (pb);

      gtk_widget_set_name (image, "yad-dialog-image");
      gtk_widget_set_halign (image, GTK_ALIGN_CENTER);
      gtk_widget_set_valign (image, GTK_ALIGN_START);
    }

  /* create text label */
  if (options.data.dialog_text)
    {
      /* for dnd's tooltip we don't need text label */
      if (options.mode != YAD_MODE_DND || !options.dnd_data.tooltip)
        {
          gchar *buf = g_strcompress (options.data.dialog_text);

          text = gtk_label_new (NULL);
          gtk_widget_set_name (text, "yad-dialog-label");
          gtk_label_set_line_wrap (GTK_LABEL (text), TRUE);
          gtk_label_set_selectable (GTK_LABEL (text), options.data.selectable_labels);
          gtk_widget_set_state_flags (text, GTK_STATE_FLAG_NORMAL, FALSE);
          gtk_widget_set_can_focus (text, FALSE);

          if (options.data.text_width > 0)
            gtk_label_set_width_chars (GTK_LABEL (text), options.data.text_width);

          /* set label align and justification */
          switch (options.data.text_align)
            {
            case GTK_JUSTIFY_LEFT:
            case GTK_JUSTIFY_FILL:
              gtk_label_set_xalign (GTK_LABEL (text), 0.0);
              break;
            case GTK_JUSTIFY_RIGHT:
              gtk_label_set_xalign (GTK_LABEL (text), 1.0);
              break;
            case GTK_JUSTIFY_CENTER:
              gtk_label_set_xalign (GTK_LABEL (text), 0.5);
              break;
            }
          gtk_label_set_justify (GTK_LABEL (text), options.data.text_align);

          if (!options.data.no_markup)
            {
              gtk_label_set_markup (GTK_LABEL (text), buf);
              g_signal_connect (G_OBJECT (text), "activate-link", G_CALLBACK (link_cb), NULL);
            }
          else
            gtk_label_set_text (GTK_LABEL (text), buf);
          g_free (buf);
        }
    }

  /* create main widget */
  switch (options.mode)
    {
    case YAD_MODE_APP:
      mw = app_create_widget (dlg);
      break;
    case YAD_MODE_CALENDAR:
      mw = calendar_create_widget (dlg);
      break;
    case YAD_MODE_COLOR:
      mw = color_create_widget (dlg);
      break;
    case YAD_MODE_ENTRY:
      mw = entry_create_widget (dlg);
      break;
    case YAD_MODE_FILE:
      mw = file_create_widget (dlg);
      break;
    case YAD_MODE_FONT:
      mw = font_create_widget (dlg);
      break;
    case YAD_MODE_FORM:
      mw = form_create_widget (dlg);
      break;
#ifdef HAVE_HTML
    case YAD_MODE_HTML:
      mw = html_create_widget (dlg);
      break;
#endif
    case YAD_MODE_ICONS:
      mw = icons_create_widget (dlg);
      break;
    case YAD_MODE_LIST:
      mw = list_create_widget (dlg);
      break;
    case YAD_MODE_NOTEBOOK:
      if (options.plug == -1)
        mw = notebook_create_widget (dlg);
      break;
    case YAD_MODE_PANED:
      if (options.plug == -1)
        mw = paned_create_widget (dlg);
      break;
    case YAD_MODE_PICTURE:
      mw = picture_create_widget (dlg);
      break;
    case YAD_MODE_PROGRESS:
      mw = progress_create_widget (dlg);
      break;
    case YAD_MODE_SCALE:
      mw = scale_create_widget (dlg);
      break;
    case YAD_MODE_TEXTINFO:
      mw = text_create_widget (dlg);
      break;
    default: ;
    }

  /* add expander */
  if (mw && options.data.expander)
    {
      imw = exp = gtk_expander_new_with_mnemonic (options.data.expander);
      gtk_expander_set_expanded (GTK_EXPANDER (exp), FALSE);
      gtk_container_add (GTK_CONTAINER (exp), mw);
    }
  else
    imw = mw;

  /* create layout */
  layout = gtk_box_new (GTK_ORIENTATION_VERTICAL, 5);
  box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);

  if (image)
    gtk_box_pack_start (GTK_BOX (box), image, FALSE, FALSE, 2);
  if (text)
    gtk_box_pack_start (GTK_BOX (box), text, TRUE, TRUE, 2);

  gtk_box_pack_start (GTK_BOX (layout), box, FALSE, FALSE, 0);
  if (imw)
    {
      if (options.mode == YAD_MODE_ENTRY)
        gtk_box_pack_start (GTK_BOX (layout), imw, TRUE, FALSE, 0);
      else
        gtk_box_pack_start (GTK_BOX (layout), imw, TRUE, TRUE, 0);
    }

  if (options.mode == YAD_MODE_DND)
    dnd_init (layout);

  return layout;
}

static void
realize_cb (GtkWidget *dlg, gpointer d)
{
  GdkGravity gravity = GDK_GRAVITY_NORTH_WEST;

  if (options.data.fixed)
    {
      gtk_widget_set_size_request (dlg, options.data.width, options.data.height);
      gtk_window_set_resizable (GTK_WINDOW (dlg), !options.data.fixed);
    }
  else
    {
      gint cw, ch;
      /* get current window size for gtk_window_resize */
      gtk_window_get_size (GTK_WINDOW (dlg), &cw, &ch);
      if (options.data.width == -1)
        options.data.width = cw;
      if (options.data.height == -1)
        options.data.height = ch;
      gtk_window_resize (GTK_WINDOW (dlg), options.data.width, options.data.height);
    }

  if (options.data.use_posx || options.data.use_posy)
    {
      gint ww, wh, sw, sh;
      gtk_window_get_size (GTK_WINDOW (dlg), &ww, &wh);
      gdk_window_get_geometry (gdk_get_default_root_window (), NULL, NULL, &sw, &sh);
      /* place window to specified coordinates */
      if (!options.data.use_posx)
        gtk_window_get_position (GTK_WINDOW (dlg), &options.data.posx, NULL);
      if (!options.data.use_posy)
        gtk_window_get_position (GTK_WINDOW (dlg), NULL, &options.data.posy);
      if (options.data.negx)
        {
          options.data.posx = sw - ww + options.data.posx;
          gravity = GDK_GRAVITY_NORTH_EAST;
        }
      if (options.data.negy)
        {
          options.data.posy = sh - wh + options.data.posy;
          gravity = GDK_GRAVITY_SOUTH_WEST;
        }
      if (options.data.negx && options.data.negy)
        gravity = GDK_GRAVITY_SOUTH_EAST;
      gtk_window_set_gravity (GTK_WINDOW (dlg), gravity);
      gtk_window_move (GTK_WINDOW (dlg), options.data.posx, options.data.posy);
    }
}

static GtkWidget *
create_dialog (void)
{
  GtkWidget *dlg, *vbox, *layout;

  /* create dialog window */
  dlg = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  if (options.data.splash)
    gtk_window_set_type_hint (GTK_WINDOW (dlg), GDK_WINDOW_TYPE_HINT_SPLASHSCREEN);
  gtk_window_set_title (GTK_WINDOW (dlg), options.data.dialog_title);
  gtk_widget_set_name (dlg, "yad-dialog-window");

  g_signal_connect (G_OBJECT (dlg), "delete-event", G_CALLBACK (gtk_main_quit), NULL);
  g_signal_connect (G_OBJECT (dlg), "key-press-event", G_CALLBACK (keys_cb), NULL);
  g_signal_connect (G_OBJECT (dlg), "focus-out-event", G_CALLBACK (unfocus_cb), NULL);

  /* set window icon */
  if (options.data.window_icon)
    {
      if (g_file_test (options.data.window_icon, G_FILE_TEST_EXISTS))
        gtk_window_set_icon_from_file (GTK_WINDOW (dlg), options.data.window_icon, NULL);
      else
        gtk_window_set_icon_name (GTK_WINDOW (dlg), options.data.window_icon);
    }
  else
    gtk_window_set_icon_name (GTK_WINDOW (dlg), "yad");

  /* set window borders */
  if (options.data.borders < 0)
    options.data.borders = 2;
  gtk_container_set_border_width (GTK_CONTAINER (dlg), (guint) options.data.borders);

  /* set window size and position */
  if (!options.data.maximized && !options.data.fullscreen)
    {
      if (options.data.center)
        gtk_window_set_position (GTK_WINDOW (dlg), GTK_WIN_POS_CENTER_ALWAYS);
      else if (options.data.mouse)
        gtk_window_set_position (GTK_WINDOW (dlg), GTK_WIN_POS_MOUSE);
    }

  /* set window behavior */
  if (options.data.sticky)
    gtk_window_stick (GTK_WINDOW (dlg));
  gtk_window_set_keep_above (GTK_WINDOW (dlg), options.data.ontop);
  gtk_window_set_decorated (GTK_WINDOW (dlg), !options.data.undecorated);
  gtk_window_set_skip_taskbar_hint (GTK_WINDOW (dlg), options.data.skip_taskbar);
  gtk_window_set_skip_pager_hint (GTK_WINDOW (dlg), options.data.skip_taskbar);
  gtk_window_set_accept_focus (GTK_WINDOW (dlg), options.data.focus);

  /* create box */
  vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
  gtk_container_add (GTK_CONTAINER (dlg), vbox);

  layout = create_layout (dlg);

  /* create timeout indicator widget */
  if (options.data.timeout)
    {
      GtkWidget *cbox = NULL, *topb = NULL;

      if (G_LIKELY (options.data.to_indicator) && strcasecmp (options.data.to_indicator, "none") != 0)
        {
          topb = gtk_progress_bar_new ();
          gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (topb), 1.0);
          gtk_widget_set_name (topb, "yad-timeout-indicator");
        }

      /* add timeout indicator */
      if (topb)
        {
          if (strcasecmp (options.data.to_indicator, "top") == 0)
            {
              gtk_orientable_set_orientation (GTK_ORIENTABLE (topb), GTK_ORIENTATION_HORIZONTAL);
              cbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
              gtk_box_pack_start (GTK_BOX (cbox), topb, FALSE, FALSE, 2);
              gtk_box_pack_end (GTK_BOX (cbox), layout, TRUE, TRUE, 0);
            }
          else if (strcasecmp (options.data.to_indicator, "bottom") == 0)
            {
              gtk_orientable_set_orientation (GTK_ORIENTABLE (topb), GTK_ORIENTATION_HORIZONTAL);
              cbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
              gtk_box_pack_start (GTK_BOX (cbox), layout, TRUE, TRUE, 0);
              gtk_box_pack_end (GTK_BOX (cbox), topb, FALSE, FALSE, 2);
            }
          else if (strcasecmp (options.data.to_indicator, "left") == 0)
            {
              gtk_orientable_set_orientation (GTK_ORIENTABLE (topb), GTK_ORIENTATION_VERTICAL);
              gtk_progress_bar_set_inverted (GTK_PROGRESS_BAR (topb), TRUE);
              cbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
              gtk_box_pack_start (GTK_BOX (cbox), topb, FALSE, FALSE, 2);
              gtk_box_pack_end (GTK_BOX (cbox), layout, TRUE, TRUE, 0);
            }
          else if (strcasecmp (options.data.to_indicator, "right") == 0)
            {
              gtk_orientable_set_orientation (GTK_ORIENTABLE (topb), GTK_ORIENTATION_VERTICAL);
              gtk_progress_bar_set_inverted (GTK_PROGRESS_BAR (topb), TRUE);
              cbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
              gtk_box_pack_start (GTK_BOX (cbox), layout, TRUE, TRUE, 0);
              gtk_box_pack_end (GTK_BOX (cbox), topb, FALSE, FALSE, 2);
            }

#ifndef STANDALONE
          if (g_settings_get_boolean (settings, "show-remain"))
#else
          if (SHOW_REMAIN)
#endif
            {
              gchar *lbl = g_strdup_printf (_("%d sec"), options.data.timeout);
              gtk_progress_bar_set_show_text (GTK_PROGRESS_BAR (topb), TRUE);
              gtk_progress_bar_set_text (GTK_PROGRESS_BAR (topb), lbl);
              g_free (lbl);
            }
        }
      else
        {
          cbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
          gtk_box_pack_start (GTK_BOX (cbox), layout, TRUE, TRUE, 0);
        }

      if (cbox)
        gtk_box_pack_start (GTK_BOX (vbox), cbox, TRUE, TRUE, 0);

      /* set timeout handler */
      g_timeout_add_seconds (1, timeout_cb, topb);
    }
  else
    gtk_box_pack_start (GTK_BOX (vbox), layout, TRUE, TRUE, 0);

#ifdef HAVE_HTML
  /* enable no-buttons mode if --browser is specified and sets no custom buttons */
  if (options.mode == YAD_MODE_HTML && options.html_data.browser && !options.data.buttons)
    options.data.no_buttons = TRUE;
#endif

  if (!options.data.no_buttons)
    {
      GtkWidget *btn;
      /* create buttons container */
      GtkWidget *bbox = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL);
      gtk_box_set_homogeneous (GTK_BOX (bbox), TRUE);
      gtk_container_set_border_width (GTK_CONTAINER (bbox), 2);
      gtk_box_set_spacing (GTK_BOX (bbox), 5);
      gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), options.data.buttons_layout);

      /* add buttons */
      if (options.data.buttons)
        {
          GSList *tmp = options.data.buttons;
          do
            {
              YadButton *b = (YadButton *) tmp->data;

              btn = gtk_button_new ();
              gtk_container_add (GTK_CONTAINER (btn), get_label (b->name, 2, btn));
              g_object_set_data (G_OBJECT (btn), "resp", GINT_TO_POINTER (b->response));
              g_signal_connect (G_OBJECT (btn), "clicked", G_CALLBACK (btn_cb), b->cmd);
              gtk_box_pack_start (GTK_BOX (bbox), btn, TRUE, TRUE, 0);

              tmp = tmp->next;
            }
          while (tmp != NULL);
        }
      else
        {
          if (options.mode == YAD_MODE_PROGRESS || options.mode == YAD_MODE_DND || options.mode == YAD_MODE_PICTURE)
            {
              /* add close button */
              btn = gtk_button_new ();
              gtk_container_add (GTK_CONTAINER (btn), get_label ("yad-close", 2, btn));
              g_object_set_data (G_OBJECT (btn), "resp", GINT_TO_POINTER (YAD_RESPONSE_OK));
              g_signal_connect (G_OBJECT (btn), "clicked", G_CALLBACK (btn_cb), NULL);
              gtk_box_pack_start (GTK_BOX (bbox), btn, TRUE, TRUE, 0);

            }
          else
            {
              /* add cancel button */
              btn = gtk_button_new ();
              gtk_container_add (GTK_CONTAINER (btn), get_label ("yad-cancel", 2, btn));
              g_object_set_data (G_OBJECT (btn), "resp", GINT_TO_POINTER (YAD_RESPONSE_CANCEL));
              g_signal_connect (G_OBJECT (btn), "clicked", G_CALLBACK (btn_cb), NULL);
              gtk_box_pack_start (GTK_BOX (bbox), btn, TRUE, TRUE, 0);

              /*add ok button */
              btn = gtk_button_new ();
              gtk_container_add (GTK_CONTAINER (btn), get_label ("yad-ok", 2, btn));
              g_object_set_data (G_OBJECT (btn), "resp", GINT_TO_POINTER (YAD_RESPONSE_OK));
              g_signal_connect (G_OBJECT (btn), "clicked", G_CALLBACK (btn_cb), NULL);
              gtk_box_pack_start (GTK_BOX (bbox), btn, TRUE, TRUE, 0);
            }
        }
      /* add buttons box to main window */
      gtk_box_pack_start (GTK_BOX (vbox), bbox, FALSE, FALSE, 0);
    }

  /* show widgets */
  gtk_widget_show_all (vbox);

  /* parse geometry or move window, if given. must be after showing widget */
  if (!options.data.maximized && !options.data.fullscreen)
    {
      parse_geometry ();
      g_signal_connect (G_OBJECT (dlg), "realize", G_CALLBACK (realize_cb), NULL);
      gtk_widget_show_all (dlg);
    }
  else
    {
      gtk_widget_show (dlg);
      /* set maximized or fixed size after showing widget */
      if (options.data.maximized)
        gtk_window_maximize (GTK_WINDOW (dlg));
      else if (options.data.fullscreen)
        gtk_window_fullscreen (GTK_WINDOW (dlg));
    }

  /* print xid */
  if (is_x11 && options.print_xid)
    {
      FILE *xf;

      if (options.xid_file)
        xf = fopen (options.xid_file, "w");
      else
        xf = stderr;

      if (xf)
        {
          fprintf (xf, "0x%lX\n", GDK_WINDOW_XID (gtk_widget_get_window (dlg)));

          if (options.xid_file)
            fclose (xf);
          else
            fflush (xf);
        }
    }

  return dlg;
}

static void
create_plug (void)
{
  GtkWidget *win, *box;

  tabs = get_tabs (options.plug, FALSE);
  while (!tabs)
    {
      usleep (1000);
      tabs = get_tabs (options.plug, FALSE);
    }

  while (!tabs[0].xid)
    usleep (1000);

  win = gtk_plug_new (0);
  /* set window borders */
  if (options.data.borders == -1)
    options.data.borders = (gint) gtk_container_get_border_width (GTK_CONTAINER (win));
  gtk_container_set_border_width (GTK_CONTAINER (win), (guint) options.data.borders);

  box = create_layout (win);
  if (box)
    gtk_container_add (GTK_CONTAINER (win), box);

  gtk_widget_show_all (win);

  /* add plug data */
  /* notebook/paned will count non-zero xids */
  tabs[options.tabnum].pid = getpid ();
  tabs[options.tabnum].xid = gtk_plug_get_id (GTK_PLUG (win));
  shmdt (tabs);
}

void
yad_print_result (void)
{
  switch (options.mode)
    {
    case YAD_MODE_APP:
      app_print_result ();
      break;
    case YAD_MODE_CALENDAR:
      calendar_print_result ();
      break;
    case YAD_MODE_COLOR:
      color_print_result ();
      break;
    case YAD_MODE_ENTRY:
      entry_print_result ();
      break;
    case YAD_MODE_FILE:
      file_print_result ();
      break;
    case YAD_MODE_FONT:
      font_print_result ();
      break;
    case YAD_MODE_FORM:
      form_print_result ();
      break;
    case YAD_MODE_LIST:
      list_print_result ();
      break;
    case YAD_MODE_NOTEBOOK:
      notebook_print_result ();
      break;
    case YAD_MODE_PANED:
      paned_print_result ();
      break;
    case YAD_MODE_SCALE:
      scale_print_result ();
      break;
    case YAD_MODE_TEXTINFO:
      text_print_result ();
      break;
    default:;
    }
}

gint
main (gint argc, gchar ** argv)
{
  GOptionContext *ctx;
  GError *err = NULL;
  gint w, h;
  gchar *str;

  setlocale (LC_ALL, "");

#ifdef ENABLE_NLS
  bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
  bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
  textdomain (GETTEXT_PACKAGE);
#endif

  gtk_init (&argc, &argv);
  g_set_application_name ("YAD");

#ifndef STANDALONE
  settings = g_settings_new ("yad.settings");
  sv_settings = g_settings_new ("yad.sourceview");
#endif

  yad_icon_theme = gtk_icon_theme_get_default ();

  yad_options_init ();

  ctx = yad_create_context ();
  /* parse YAD_OPTIONS */
  if (g_getenv ("YAD_OPTIONS"))
    {
      gchar *cmd, **args = NULL;
      gint cnt;

      cmd = g_strdup_printf ("yad %s", g_getenv ("YAD_OPTIONS"));

      if (g_shell_parse_argv (cmd, &cnt, &args, &err))
        {
          g_option_context_parse (ctx, &cnt, &args, &err);
          if (err)
            {
              g_printerr (_("Unable to parse YAD_OPTIONS: %s\n"), err->message);
              g_error_free (err);
              err = NULL;
            }
        }
      else
        {
          g_printerr (_("Unable to parse YAD_OPTIONS: %s\n"), err->message);
          g_error_free (err);
          err = NULL;
        }

      g_free (cmd);
    }
  /* parse command line */
  g_option_context_parse (ctx, &argc, &argv, &err);
  if (err)
    {
      g_printerr (_("Unable to parse command line: %s\n"), err->message);
      return -1;
    }
  yad_set_mode ();

  /* check for current GDK backend */
#ifdef GDK_WINDOWING_X11
  if (GDK_IS_X11_DISPLAY (gdk_display_get_default ()))
    is_x11 = TRUE;
#endif

  /* parse custom css */
  if (options.css)
    {
      GtkCssProvider *css = gtk_css_provider_new ();

      if (g_file_test (options.css, G_FILE_TEST_EXISTS))
        gtk_css_provider_load_from_path (css, options.css, NULL);
      else
        gtk_css_provider_load_from_data (css, options.css, -1, NULL);
      gtk_style_context_add_provider_for_screen (gdk_screen_get_default (), GTK_STYLE_PROVIDER (css),
                                                 GTK_STYLE_PROVIDER_PRIORITY_USER);
      g_object_unref (css);
    }
  else if (options.gtkrc_file)
    {
      GtkCssProvider *css = gtk_css_provider_new ();

      if (options.debug)
        g_printerr (_("WARNING: Using gtkrc option is deprecated, please use --css instead\n"));

      gtk_css_provider_load_from_path (css, options.gtkrc_file, NULL);
      gtk_style_context_add_provider_for_screen (gdk_screen_get_default (), GTK_STYLE_PROVIDER (css),
                                                 GTK_STYLE_PROVIDER_PRIORITY_USER);
      g_object_unref (css);
    }

  /* set default icons and icon theme */
  if (options.data.icon_theme)
    gtk_icon_theme_set_custom_theme (yad_icon_theme, options.data.icon_theme);
  gtk_icon_size_lookup (GTK_ICON_SIZE_DIALOG, &w, &h);
  big_fallback_image = gtk_icon_theme_load_icon (yad_icon_theme, "yad", MIN (w, h), GTK_ICON_LOOKUP_GENERIC_FALLBACK, NULL);
  gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &w, &h);
  small_fallback_image = gtk_icon_theme_load_icon (yad_icon_theme, "yad", MIN (w, h), GTK_ICON_LOOKUP_GENERIC_FALLBACK, NULL);

  /* correct separators */
  str = g_strcompress (options.common_data.separator);
  options.common_data.separator = str;
  str = g_strcompress (options.common_data.item_separator);
  options.common_data.item_separator = str;

  /* loads an extra arguments, if specified */
  if (options.rest_file)
    {
      GIOChannel *ioc;
      gchar *buf;
      guint len, line = 0;

      g_strfreev (options.extra_data);
      options.extra_data = NULL;

      ioc = g_io_channel_new_file (options.rest_file, "r", NULL);
      while (TRUE)
        {
          gint status = g_io_channel_read_line (ioc, &buf, NULL, NULL, NULL);

          if (status != G_IO_STATUS_NORMAL)
            break;

          /* remove \n at the end of string */
          len = strlen (buf);
          if (buf[len - 1] == '\n')
            buf[len - 1] = '\0';

          /* add line to arguments array */
          options.extra_data = g_realloc (options.extra_data, (line + 2) * sizeof (gchar *));
          options.extra_data[line] = g_strcompress (buf);
          options.extra_data[line + 1] = NULL;

          g_free (buf);
          line++;
        }
      g_io_channel_shutdown (ioc, FALSE, NULL);
    }

#ifndef G_OS_WIN32
  /* add YAD_PID variable */
  str = g_strdup_printf ("%d", getpid ());
  g_setenv ("YAD_PID", str, TRUE);
  /* set signal handlers */
  signal (SIGUSR1, sa_usr1);
  signal (SIGUSR2, sa_usr2);
#endif

  if (!is_x11 && options.plug != -1)
    {
      options.plug = -1;
      if (options.debug)
        g_printerr (_("WARNING: --plug mode not supported outside X11\n"));
    }

  /* plug mode */
  if (options.plug != -1)
    {
      create_plug ();
      gtk_main ();
      shmdt (tabs);
      return ret;
    }

  if (!is_x11)
    {
      if (options.mode == YAD_MODE_NOTEBOOK || options.mode == YAD_MODE_PANED
#ifdef HAVE_TRAY
          || options.mode == YAD_MODE_NOTIFICATION
#endif
         )
        {
          g_printerr (_("WARNING: This mode not supported outside X11\n"));
          return 1;
        }
    }

  switch (options.mode)
    {
    case YAD_MODE_ABOUT:
      ret = yad_about ();
      break;

    case YAD_MODE_VERSION:
      g_print ("%s (GTK+ %d.%d.%d)\n", VERSION, gtk_major_version, gtk_minor_version, gtk_micro_version);
      break;

#ifdef HAVE_TRAY
    case YAD_MODE_NOTIFICATION:
      ret = yad_notification_run ();
      break;
#endif

    case YAD_MODE_PRINT:
      ret = yad_print_run ();
      break;

    default:
      dialog = create_dialog ();

      if (is_x11)
        {
          /* add YAD_XID variable */
          str = g_strdup_printf ("0x%lX", GDK_WINDOW_XID (gtk_widget_get_window (dialog)));
          g_setenv ("YAD_XID", str, TRUE);
        }

      /* make some specific init actions */
      if (options.mode == YAD_MODE_NOTEBOOK)
        notebook_swallow_childs ();
      else if (options.mode == YAD_MODE_PANED)
        paned_swallow_childs ();
      else if (options.mode == YAD_MODE_TEXTINFO)
        text_goto_line ();
      else if (options.mode == YAD_MODE_PICTURE)
        {
          if (options.picture_data.size == YAD_PICTURE_FIT)
            picture_fit_to_window ();
        }

      if (text && options.data.selectable_labels)
        gtk_label_select_region (GTK_LABEL (text), 0, 0);

      /* run main loop */
      gtk_main ();

      /* print results */
      if (ret != YAD_RESPONSE_TIMEOUT && ret != YAD_RESPONSE_ESC)
        {
          if (options.data.always_print)
            yad_print_result ();
          else
            {
              /* standard OK button pressed */
              if (ret == YAD_RESPONSE_OK && options.data.buttons == NULL)
                yad_print_result ();
              /* custom even button pressed */
              else if (!(ret & 1))
                yad_print_result ();
            }
        }
#ifndef G_OS_WIN32
      if (options.mode == YAD_MODE_NOTEBOOK)
        notebook_close_childs ();
      else if (options.mode == YAD_MODE_PANED)
        paned_close_childs ();
      /* autokill option for progress dialog */
      if (!options.kill_parent)
        {
          if (options.mode == YAD_MODE_PROGRESS && options.progress_data.autokill && ret != YAD_RESPONSE_OK)
            kill (getppid (), SIGHUP);
        }
#endif
    }

#ifndef G_OS_WIN32
  /* NSIG defined in signal.h */
  if (options.kill_parent > 0 && options.kill_parent < NSIG)
    kill (getppid (), options.kill_parent);
#endif

  return ret;
}