/* Copyright 2011 Pierre Ossman <ossman@cendio.se> for Cendio AB
 * Copyright (C) 2014-2024 m-privacy GmbH
 * 
 * This 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 software 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 software; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
 * USA.
 */

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

#include <stdlib.h>

#include <list>

#include <rdr/types.h>
#include <rfb/encodings.h>
#include <rfb/CMsgWriter.h>

#include <rfb/MP.h>

#ifdef HAVE_GNUTLS
#include <rfb/Security.h>
#include <rfb/SecurityClient.h>
#include <rfb/CSecurityTLS.h>
#endif

#include "OptionsDialog.h"
#include "fltk_layout.h"
#include "i18n.h"
#include "menukey.h"
#include "parameters.h"

#include "vncviewer.h"
#include "printing.h"
#include "sound_handler.h"
#include "MagicUrlHelper.h"
#include "dpi_scale.h"

#include <FL/Fl_Tabs.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Check_Button.H>
#include <FL/Fl_Return_Button.H>
#include <FL/Fl_Round_Button.H>
#include <FL/Fl_Int_Input.H>
#include <FL/Fl_Choice.H>
#include <FL/fl_ask.H>
#include <FL/Fl_Box.H>

#include <rfb/LogWriter.h>

using namespace std;
using namespace rdr;
using namespace rfb;

static rfb::LogWriter vlog("OptionsDialog");

std::map<OptionsCallback*, void*> OptionsDialog::callbacks;

static char msg[256];
static char audio_msg[256];
int oldSoundSupport;
static char standard_browser_always_ask_msg[256];
static char setStandardBrowserMessage[256];
static char magicUrlIsNotInstalledMessage[256];

inline char* get_dialog_title() {
  snprintf(msg, 256, _("%s: Connection Options"), productName.getData());
  return msg;
}

OptionsDialog::OptionsDialog(CConn * _cc)
  : Fl_Window(scale(600), scale(600), get_dialog_title()), cc(_cc)
{
  int x, y;
  Fl_Button *button;

  Fl_Tabs *tabs = new Fl_Tabs(OUTER_MARGIN, OUTER_MARGIN,
                             w() - OUTER_MARGIN*2,
                             h() - OUTER_MARGIN*2 - INNER_MARGIN - BUTTON_HEIGHT);

  {
    int tx, ty, tw, th;

    tabs->client_area(tx, ty, tw, th, TABS_HEIGHT);

    createInfoPage(tx, ty, tw, th);
    createCompressionPage(tx, ty, tw, th);
    createSecurityPage(tx, ty, tw, th);
    createInputPage(tx, ty, tw, th);
    createScreenPage(tx, ty, tw, th);
    if (!tgproCC)
      createPrintPage(tx, ty, tw, th);
    createSoundPage(tx, ty, tw, th);
    if (!tgproCC)
      createStandardBrowserPage(tx, ty, tw, th);
  }

  tabs->end();

  x = w() - BUTTON_WIDTH * 2 - INNER_MARGIN - OUTER_MARGIN;
  y = h() - BUTTON_HEIGHT - OUTER_MARGIN;

  button = new Fl_Button(x, y, BUTTON_WIDTH, BUTTON_HEIGHT, _("Cancel"));
  button->callback(this->handleCancel, this);
  scaleWidgetFonts(button);

  x += BUTTON_WIDTH + INNER_MARGIN;

  button = new Fl_Return_Button(x, y, BUTTON_WIDTH, BUTTON_HEIGHT, _("OK"));
  button->callback(this->handleOK, this);
  scaleWidgetFonts(button);

  callback(this->handleCancel, this);

  oldSoundSupport = soundSupport;

  set_modal();
}


OptionsDialog::~OptionsDialog()
{
}


void OptionsDialog::showDialog(CConn * _cc)
{
  static OptionsDialog *dialog = NULL;
#ifdef WIN32
  POINT cursorPos;
#endif

  if (!dialog)
    dialog = new OptionsDialog(_cc);
  else if (_cc)
    dialog->cc = _cc;

  dialog->updateConnectionInfoOnMiscPage();

#ifdef WIN32
  if(GetCursorPos(&cursorPos))
    dialog->position(cursorPos.x, cursorPos.y);
#endif

  if (dialog->shown())
    return;

  dialog->show();
}


void OptionsDialog::addCallback(OptionsCallback *cb, void *data)
{
  callbacks[cb] = data;
}


void OptionsDialog::removeCallback(OptionsCallback *cb)
{
  callbacks.erase(cb);
}


void OptionsDialog::show(void)
{
  /* show() gets called for raise events as well */
  if (!shown())
    loadOptions();

  Fl_Window::show();
}


void OptionsDialog::loadOptions(void)
{
  /* Compression */
  mpCheckbox->value(!noMP);

  int count = 0;
  for (int i = MP_COMPRESSION_MIN; i <= MP_COMPRESSION_MAX; i++) {
    if (mpCompressionValid(i)) {
      if (i == mpCompression) {
        mpCompressionChoice->value(count);
        break;
      }
      count++;
    }
  }
  handleMP(mpCheckbox, this);

  /* Input */
  const char *menuKeyBuf;

  viewOnlyCheckbox->value(viewOnly);
  emulateMBCheckbox->value(emulateMiddleButton);
  acceptClipboardCheckbox->value(acceptClipboard);
  sendClipboardCheckbox->value(sendClipboard);
  if (enforceClipboard) {
    acceptClipboardCheckbox->deactivate();
    sendClipboardCheckbox->deactivate();
  }
#if !defined(WIN32) && !defined(__APPLE__)
  sendPrimaryCheckbox->value(sendPrimary);
  if (enforceClipboard) {
    sendPrimaryCheckbox->deactivate();
  }
#endif
  systemKeysCheckbox->value(fullscreenSystemKeys);
  if (!tgproCC)
    autotransferSupportCheckbox->value(autotransferSupport);

  menuKeyChoice->value(0);

  menuKeyBuf = menuKey;
  for (int i = 0; i < getMenuKeySymbolCount(); i++)
    if (!strcmp(getMenuKeySymbols()[i].name, menuKeyBuf))
      menuKeyChoice->value(i + 1);

  /* Screen */
  int width, height;

  if (sscanf((const char*)desktopSize, "%dx%d", &width, &height) != 2) {
    desktopSizeCheckbox->value(false);
    desktopWidthInput->value("1024");
    desktopHeightInput->value("768");
  } else {
    char buf[32];
    desktopSizeCheckbox->value(true);
    snprintf(buf, sizeof(buf), "%d", width);
    desktopWidthInput->value(buf);
    snprintf(buf, sizeof(buf), "%d", height);
    desktopHeightInput->value(buf);
  }
  remoteResizeCheckbox->value(remoteResize);
  fullScreenCheckbox->value(fullScreen);
  fullScreenAllMonitorsCheckbox->value(fullScreenAllMonitors);

  handleDesktopSize(desktopSizeCheckbox, this);

  /* Sound */
  soundCheckbox->value(soundSupport);

  if (!tgproCC) {
    /* Print */
    printSupportCheckbox->value(printSupport);
    quickPrintCheckbox->value(quickPrint);
    handlePrintSupport(printSupportCheckbox, this);

    /* Standard browser */
    standardBrowserPromptCheckbox->value(standardBrowserPrompt);
  }
}


void OptionsDialog::storeOptions(void)
{
  /* Compression */
  noMP.setParam(!mpCheckbox->value());

  if (mpCompressionChoice->text()) {
    for (int i = MP_COMPRESSION_MIN; i <= MP_COMPRESSION_MAX; i++) {
      if (mpCompressionValid(i) && !strcmp(mpCompressionChoice->text(), mpCompressionName(i))) {
        if (!mpCompression.setParam(i))
          vlog.debug("failed to set mpCompression value %i", i);
        break;
      }
    }
  }

  /* Input */
  viewOnly.setParam(viewOnlyCheckbox->value());
  emulateMiddleButton.setParam(emulateMBCheckbox->value());
  acceptClipboard.setParam(acceptClipboardCheckbox->value());
  sendClipboard.setParam(sendClipboardCheckbox->value());
#if !defined(WIN32) && !defined(__APPLE__)
  sendPrimary.setParam(sendPrimaryCheckbox->value());
#endif
  fullscreenSystemKeys.setParam(systemKeysCheckbox->value());

  if (!tgproCC)
    autotransferSupport.setParam(autotransferSupportCheckbox->value());

  if (menuKeyChoice->value() == 0)
    menuKey.setParam("");
  else {
    menuKey.setParam(menuKeyChoice->text());
  }

  /* Screen */
  int width, height;

  if (desktopSizeCheckbox->value() &&
      (sscanf(desktopWidthInput->value(), "%d", &width) == 1) &&
      (sscanf(desktopHeightInput->value(), "%d", &height) == 1)) {
    char buf[64];
    snprintf(buf, sizeof(buf), "%dx%d", width, height);
    desktopSize.setParam(buf);
  } else {
    desktopSize.setParam("");
  }
  remoteResize.setParam(remoteResizeCheckbox->value());
  fullScreen.setParam(fullScreenCheckbox->value());
  fullScreenAllMonitors.setParam(fullScreenAllMonitorsCheckbox->value());

  if (mpScalingChoice->text()) {
    U16 value;

    mpScaling.setParam(atoi(mpScalingChoice->text()));
    vlog.debug("Send mpScaling");
    value = mpScaling;
    if (!cc->writer()->sendSignal(MPSCALING_SIGNAL_ID, &value, 2))
      vlog.debug("sendSignal(MPSCALING_SIGNAL_ID) failed");
  }

  soundSupport.setParam(soundCheckbox->value());
#if defined(WIN32) || defined(__APPLE__)
  if (cc) {
    if (soundSupport && !oldSoundSupport) {
      vlog.debug("Trying to start Pulseaudio");
      if (!sound::start_sound(cc->paPort)) {
        vlog.error("failed to start Pulseaudio, disabling soundSupport");
        soundSupport.setParam(false);
        cc->setSoundSupport(false);
      } else {
        cc->setSoundSupport(true);
      }
    } else if (!soundSupport && oldSoundSupport) {
      if (cc->paPort == 4713) {
        vlog.debug("Trying to stop Pulseaudio");
        sound::stop_sound();
        sound::kill_all_pulseaudio_processes();
      }
      cc->setSoundSupport(false);
    }
  } else {
    vlog.error("No CConn found, failed to start or stop Pulseaudio");
  }
#endif
  oldSoundSupport = soundSupport;
  if (!tgproCC) {
    printSupport.setParam(printSupportCheckbox->value());
    quickPrint.setParam(quickPrintCheckbox->value());
    standardBrowserPrompt.setParam((bool) standardBrowserPromptCheckbox->value());
  }

  std::map<OptionsCallback*, void*>::const_iterator iter;

  for (iter = callbacks.begin();iter != callbacks.end();++iter)
    iter->first(iter->second);
}


void OptionsDialog::createCompressionPage(int tx, int ty, int tw, int th)
{
  Fl_Group *group = new Fl_Group(tx, ty, tw, th, _("Compression"));

  int orig_tx;
  tx += OUTER_MARGIN;
  ty += OUTER_MARGIN;

  /* Two columns */
  orig_tx = tx;
  /* Back to normal */
  tx = orig_tx;
  ty += INNER_MARGIN;

  mpCheckbox = new Fl_Check_Button(LBLRIGHT(tx, ty,
                                              CHECK_MIN_WIDTH,
                                              CHECK_HEIGHT,
                                              _("Allow TightMP encoding")));
  mpCheckbox->callback(handleMP, this);
  ty += CHECK_HEIGHT + TIGHT_MARGIN;

  mpCompressionChoice = new Fl_Choice(LBLLEFT(tx, ty, scale(380), CHOICE_HEIGHT, _("TightMP compression: ")));
  for (int i = MP_COMPRESSION_MIN; i <= MP_COMPRESSION_MAX; i++) {
    if (mpCompressionValid(i))
      fltk_menu_add(mpCompressionChoice, mpCompressionName(i), 0, NULL, 0, 0);
  }
  ty += CHOICE_HEIGHT + TIGHT_MARGIN;

  scaleWidgetFonts(group);

  group->end();
}


void OptionsDialog::createSecurityPage(int tx, int ty, int tw, int th)
{
#ifdef HAVE_GNUTLS
  if (!tgproCC) {
    Fl_Group *group = new Fl_Group(tx, ty, tw, th, _("Security"));

    int orig_tx;
    int width, height;

    tx += OUTER_MARGIN;
    ty += OUTER_MARGIN;

    width = tw - OUTER_MARGIN * 2;

    orig_tx = tx;

    /* Auto Transfer */
    ty += GROUP_LABEL_OFFSET;
    height = GROUP_MARGIN * 2 + CHECK_HEIGHT * 1;
    autotransferGroup = new Fl_Group(tx, ty, width, height, _("Auto download"));
    autotransferGroup->box(FL_ENGRAVED_BOX);
    autotransferGroup->align(FL_ALIGN_LEFT | FL_ALIGN_TOP);

    tx += GROUP_MARGIN;
    ty += GROUP_MARGIN;
    autotransferSupportCheckbox = new Fl_Check_Button(LBLRIGHT(tx, ty,
                                                     CHECK_MIN_WIDTH,
                                                     CHECK_HEIGHT,
                                                     _("Enable auto download")));

    autotransferGroup->end();

    /* Back to normal */
    tx = orig_tx;
    ty += INNER_MARGIN;

    scaleWidgetFonts(group);

    group->end();
  }
#endif
}


void OptionsDialog::createInputPage(int tx, int ty, int tw, int th)
{
  Fl_Group *group = new Fl_Group(tx, ty, tw, th, _("Input"));

  tx += OUTER_MARGIN;
  ty += OUTER_MARGIN;

  /* Group start block  */
  ty += GROUP_LABEL_OFFSET;
  int height = GROUP_MARGIN * 2 + TIGHT_MARGIN * 2 + CHECK_HEIGHT * 3;
  int width = tw - OUTER_MARGIN * 2;
  clipboardGroup = new Fl_Group(tx, ty, width, height, _("Clipboard"));
  clipboardGroup->box(FL_ENGRAVED_BOX);
  clipboardGroup->align(FL_ALIGN_LEFT | FL_ALIGN_TOP);
  tx += GROUP_MARGIN;
  ty += GROUP_MARGIN;
  /* Group start block end */

  acceptClipboardCheckbox = new Fl_Check_Button(LBLRIGHT(tx, ty,
                                                         CHECK_MIN_WIDTH,
                                                         CHECK_HEIGHT,
                                                         _("Get clipboard from server")));
  ty += CHECK_HEIGHT + TIGHT_MARGIN;

  sendClipboardCheckbox = new Fl_Check_Button(LBLRIGHT(tx, ty,
                                                       CHECK_MIN_WIDTH,
                                                       CHECK_HEIGHT,
                                                       _("Send clipboard to server")));
  ty += CHECK_HEIGHT + TIGHT_MARGIN;

#if !defined(WIN32) && !defined(__APPLE__)
  sendPrimaryCheckbox = new Fl_Check_Button(LBLRIGHT(tx, ty,
                                            CHECK_MIN_WIDTH,
                                            CHECK_HEIGHT,
                                            _("Send selected text as clipboard")));
  ty += CHECK_HEIGHT + TIGHT_MARGIN;
#endif

  /* Group end block  */
  clipboardGroup->end();
  ty += GROUP_MARGIN - TIGHT_MARGIN;
  tx -= GROUP_MARGIN;
  ty += INNER_MARGIN;
  /* Group end block end */

  ty += CHECK_HEIGHT + TIGHT_MARGIN;

  /* Group start block  */
  ty += GROUP_LABEL_OFFSET;
  height = GROUP_MARGIN * 2 + TIGHT_MARGIN * 2 + CHECK_HEIGHT * 2;
  width = tw - OUTER_MARGIN * 2;
  mouseGroup = new Fl_Group(tx, ty, width, height, _("Mouse"));
  mouseGroup->box(FL_ENGRAVED_BOX);
  mouseGroup->align(FL_ALIGN_LEFT | FL_ALIGN_TOP);
  tx += GROUP_MARGIN;
  ty += GROUP_MARGIN;
  /* Group start block end */

  emulateMBCheckbox = new Fl_Check_Button(LBLRIGHT(tx, ty,
                                                   CHECK_MIN_WIDTH,
                                                   CHECK_HEIGHT,
                                                   _("Emulate middle mouse click")));

  ty += CHECK_HEIGHT + TIGHT_MARGIN;

  dotCursorCheckbox = new Fl_Check_Button(LBLRIGHT(tx, ty,
                                                   CHECK_MIN_WIDTH,
                                                   CHECK_HEIGHT,
                                                   _("Display dot when no mouse pointer is present")));
  ty += CHECK_HEIGHT + TIGHT_MARGIN;

  /* Group end block  */
  clipboardGroup->end();
  ty += GROUP_MARGIN - TIGHT_MARGIN;
  tx -= GROUP_MARGIN;
  ty += INNER_MARGIN;
  /* Group end block end */

  viewOnlyCheckbox = new Fl_Check_Button(LBLRIGHT(tx, ty,
                                                  CHECK_MIN_WIDTH,
                                                  CHECK_HEIGHT,
                                                  _("View only (lock mouse and keyboard)")));
  ty += CHECK_HEIGHT + TIGHT_MARGIN;

  systemKeysCheckbox = new Fl_Check_Button(LBLRIGHT(tx, ty,
                                                    CHECK_MIN_WIDTH,
                                                    CHECK_HEIGHT,
                                                    _("Pass system keys directly to server (full screen)")));
  ty += CHECK_HEIGHT + TIGHT_MARGIN;

  menuKeyChoice = new Fl_Choice(LBLLEFT(tx, ty, scale(150), CHOICE_HEIGHT, _("Menu key")));

  fltk_menu_add(menuKeyChoice, _("None"), 0, NULL, (void*)0, FL_MENU_DIVIDER);
  for (int i = 0; i < getMenuKeySymbolCount(); i++)
    fltk_menu_add(menuKeyChoice, getMenuKeySymbols()[i].name, 0, NULL, 0, 0);

  ty += CHOICE_HEIGHT + TIGHT_MARGIN;

  scaleWidgetFonts(group);

  group->end();
}


void OptionsDialog::createScreenPage(int tx, int ty, int tw, int th)
{
  int x;

  Fl_Group *group = new Fl_Group(tx, ty, tw, th, _("Screen"));

  tx += OUTER_MARGIN;
  ty += OUTER_MARGIN;

  desktopSizeCheckbox = new Fl_Check_Button(LBLRIGHT(tx, ty,
                                                     CHECK_MIN_WIDTH,
                                                     CHECK_HEIGHT,
                                                     _("Resize remote session upon connection")));
  desktopSizeCheckbox->callback(handleDesktopSize, this);
  ty += CHECK_HEIGHT + TIGHT_MARGIN;

  desktopWidthInput = new Fl_Int_Input(tx + INDENT, ty, 50, INPUT_HEIGHT);
  x = desktopWidthInput->x() + desktopWidthInput->w() + \
      gui_str_len("x") + 3 * 2;
  desktopHeightInput = new Fl_Int_Input(x, ty, 50, INPUT_HEIGHT, "x");
  ty += INPUT_HEIGHT + TIGHT_MARGIN;

  remoteResizeCheckbox = new Fl_Check_Button(LBLRIGHT(tx, ty,
                                                      CHECK_MIN_WIDTH,
                                                      CHECK_HEIGHT,
                                                      _("Resize remote session to fit viewer window")));
  ty += CHECK_HEIGHT + TIGHT_MARGIN;

  fullScreenCheckbox = new Fl_Check_Button(LBLRIGHT(tx, ty,
                                                  CHECK_MIN_WIDTH,
                                                  CHECK_HEIGHT,
                                                  _("Full-screen mode")));
  ty += CHECK_HEIGHT + TIGHT_MARGIN;

  fullScreenAllMonitorsCheckbox = new Fl_Check_Button(LBLRIGHT(tx + INDENT, ty,
                                                      CHECK_MIN_WIDTH,
                                                      CHECK_HEIGHT,
                                                      _("Enable full-screen mode over all monitors")));
  ty += CHECK_HEIGHT + TIGHT_MARGIN;

  mpScalingChoice = new Fl_Choice(LBLLEFT(tx, ty, 380, CHOICE_HEIGHT, _("Screen scaling (in percentage): ")));
  fltk_menu_add(mpScalingChoice, "75", 0, NULL, 0, 0);
  fltk_menu_add(mpScalingChoice, "88", 0, NULL, 0, 0);
  fltk_menu_add(mpScalingChoice, "100", 0, NULL, 0, 0);
  fltk_menu_add(mpScalingChoice, "112", 0, NULL, 0, 0);
  fltk_menu_add(mpScalingChoice, "125", 0, NULL, 0, 0);
  fltk_menu_add(mpScalingChoice, "150", 0, NULL, 0, 0);
  fltk_menu_add(mpScalingChoice, "175", 0, NULL, 0, 0);
  fltk_menu_add(mpScalingChoice, "200", 0, NULL, 0, 0);

  switch(mpScaling) {
    case 75:
       mpScalingChoice->value(0);
       break;
    case 88:
       mpScalingChoice->value(1);
       break;
    case 112:
       mpScalingChoice->value(3);
       break;
    case 125:
       mpScalingChoice->value(4);
       break;
    case 150:
       mpScalingChoice->value(5);
       break;
    case 175:
       mpScalingChoice->value(6);
       break;
    case 200:
       mpScalingChoice->value(7);
       break;
    default:
       mpScalingChoice->value(2);
  }
  ty += CHOICE_HEIGHT + TIGHT_MARGIN;

  scaleWidgetFonts(group);

  group->end();
}

void OptionsDialog::createPrintPage(int tx, int ty, int tw, int th) {
  Fl_Group *group = new Fl_Group(tx, ty, tw, th, _("Printing"));

  tx += OUTER_MARGIN;
  ty += OUTER_MARGIN;

  printSupportCheckbox = new Fl_Check_Button(LBLRIGHT(tx, ty,
                                                      CHECK_MIN_WIDTH,
                                                      CHECK_HEIGHT,
                                                      _("Accept print jobs from the server and print locally")));
  printSupportCheckbox->callback(handlePrintSupport, this);
  ty += CHECK_HEIGHT + TIGHT_MARGIN;

  quickPrintCheckbox = new Fl_Check_Button(LBLRIGHT(tx + INDENT, ty,
                                                    CHECK_MIN_WIDTH,
                                                    CHECK_HEIGHT,
                                                    _("Print to default printer directly (no printer selection)")));
  ty += CHECK_HEIGHT + TIGHT_MARGIN;

  scaleWidgetFonts(group);

  group->end();
}


void OptionsDialog::createSoundPage(int tx, int ty, int tw, int th)
{
  Fl_Group *group = new Fl_Group(tx, ty, tw, th, _("Sound"));

  tx += OUTER_MARGIN;
  ty += OUTER_MARGIN;

  snprintf(audio_msg, 256, _("Activate %s audio"), productName.getData());
  soundCheckbox = new Fl_Check_Button(LBLRIGHT(tx, ty,
                                               CHECK_MIN_WIDTH,
                                               CHECK_HEIGHT,
                                               audio_msg));
  ty += CHECK_HEIGHT + TIGHT_MARGIN;

  scaleWidgetFonts(group);

  group->end();
}

void OptionsDialog::createStandardBrowserPage(int tx, int ty, int tw, int th)
{
	Fl_Group *group = new Fl_Group(tx, ty, tw, th, _("Default"));

	tx += OUTER_MARGIN;
	ty += OUTER_MARGIN;

	MagicUrlHelper* magicUrlHelper = new MagicUrlHelper();

	snprintf(setStandardBrowserMessage, scale(255), _("Make %s your default browser"), productName.getData());
	Fl_Button* button = new Fl_Button(tx, ty, scale(350), BUTTON_HEIGHT, setStandardBrowserMessage);
	button->callback(this->handleSetStandardBrowser, this);

	ty += BUTTON_HEIGHT + 10;
	snprintf(standard_browser_always_ask_msg, scale(256), _("Show message at startup if %s is not the default browser"), productName.getData());
	standardBrowserPromptCheckbox = new Fl_Check_Button(LBLRIGHT(tx, ty, CHECK_MIN_WIDTH, CHECK_HEIGHT, standard_browser_always_ask_msg));

	if (magicUrlHelper->isStandardBrowser()) { // If it is already standard browser gray it out
		button->deactivate();
		standardBrowserPromptCheckbox->deactivate();
	}

	if (!magicUrlHelper->isMagicUrlInstalled()) {
		if (button->active()) {
			button->deactivate();
		}
		if (standardBrowserPromptCheckbox->active()) {
			standardBrowserPromptCheckbox->deactivate();
		}

		ty += CHECK_HEIGHT + scale(20);
		snprintf(magicUrlIsNotInstalledMessage, scale(256), _("Please install MagicUrl, in order to be able to make %s your default browser."), productName.getData());
		Fl_Box* box = new Fl_Box(tx, ty, scale(500), scale(25), magicUrlIsNotInstalledMessage);
		box->align(FL_ALIGN_INSIDE | FL_ALIGN_WRAP | FL_ALIGN_LEFT);
	}

	delete magicUrlHelper;

	scaleWidgetFonts(group);

	group->end();
}

void OptionsDialog::createInfoPage(int tx, int ty, int tw, int th)
{
  int width, height;

  Fl_Group *group = new Fl_Group(tx, ty, tw, th, _("Info"));

  tx += OUTER_MARGIN;
  ty += OUTER_MARGIN;

  /* "About viewer" group start block  */
  ty += GROUP_LABEL_OFFSET;
  int aboutViewerHeight = scale(60);
  height = GROUP_MARGIN * 2 + TIGHT_MARGIN * 2 + aboutViewerHeight;
  width = tw - OUTER_MARGIN * 2;
  aboutViewerGroup = new Fl_Group(tx, ty, width, height, _("About the viewer"));
  aboutViewerGroup->box(FL_ENGRAVED_BOX);
  aboutViewerGroup->align(FL_ALIGN_LEFT | FL_ALIGN_TOP);
  tx += GROUP_MARGIN;
  ty += GROUP_MARGIN;
  /* "About viewer" group start block end */

  Fl_Box* aboutViewerBox = new Fl_Box(tx, ty, scale(500), aboutViewerHeight, about_text());
  aboutViewerBox->align(FL_ALIGN_INSIDE | FL_ALIGN_WRAP | FL_ALIGN_LEFT);
  ty += height;

  /* "About viewer" group end block  */
  aboutViewerGroup->end();
  /* "About viewer" group end block end */

  ty += GROUP_MARGIN - TIGHT_MARGIN;
  tx -= GROUP_MARGIN;

  /* "Connection info" group start block  */
  ty += GROUP_LABEL_OFFSET;
  int connectionInfoHeight = scale(289); // More or less 17 pixels per line for scale 1 - I've personally seen a max of 16 lines (and assumed 17 now), but that might change
  height = GROUP_MARGIN * 2 + TIGHT_MARGIN * 2 + connectionInfoHeight;
  width = tw - OUTER_MARGIN * 2;
  connectionInfoGroup = new Fl_Group(tx, ty, width, height, _("Connection details"));
  connectionInfoGroup->box(FL_ENGRAVED_BOX);
  connectionInfoGroup->align(FL_ALIGN_LEFT | FL_ALIGN_TOP);
  tx += GROUP_MARGIN;
  ty += GROUP_MARGIN;
  /* "Connection info" group start block end */
  //  ty += INNER_MARGIN;
  connectionInfoBox = new Fl_Box(tx, ty, scale(500), connectionInfoHeight, cc->connectionInfo());
  connectionInfoBox->align(FL_ALIGN_INSIDE | FL_ALIGN_WRAP | FL_ALIGN_LEFT);
  ty += height;

  /* "Connection info" group end block  */
  connectionInfoGroup->end();
  /* "Connection info" group end block end */

  scaleWidgetFonts(group);

  group->end(); // Info ("Sonstiges") tab end
}

void OptionsDialog::updateConnectionInfoOnMiscPage()
{
  if (connectionInfoBox) {
    connectionInfoBox->label(cc->connectionInfo());
  }
}

void OptionsDialog::handleMP(Fl_Widget *widget, void *data)
{
  OptionsDialog *dialog = (OptionsDialog*)data;

  if (dialog->mpCheckbox->value()) {
//    dialog->mpInput->activate();
    dialog->mpCompressionChoice->activate();
  } else {
//    dialog->mpInput->deactivate();
    dialog->mpCompressionChoice->deactivate();
  }
}

void OptionsDialog::handlePrintSupport(Fl_Widget *widget, void *data)
{
  OptionsDialog *dialog = (OptionsDialog*)data;

  if (dialog->printSupportCheckbox->value()) {
    dialog->quickPrintCheckbox->activate();
  } else {
    dialog->quickPrintCheckbox->deactivate();
  }
}

void OptionsDialog::handleDesktopSize(Fl_Widget *widget, void *data)
{
  OptionsDialog *dialog = (OptionsDialog*)data;

  if (dialog->desktopSizeCheckbox->value()) {
    dialog->desktopWidthInput->activate();
    dialog->desktopHeightInput->activate();
  } else {
    dialog->desktopWidthInput->deactivate();
    dialog->desktopHeightInput->deactivate();
  }
}

void OptionsDialog::handleClipboard(Fl_Widget *widget, void *data)
{
#if !defined(WIN32) && !defined(__APPLE__)
  OptionsDialog *dialog = (OptionsDialog*)data;

  if (!dialog->acceptClipboardCheckbox->value()) {
    if (dialog->sendClipboardCheckbox->value())
      dialog->sendPrimaryCheckbox->activate();
    else
      dialog->sendPrimaryCheckbox->deactivate();
  }
#endif
}

void OptionsDialog::handleSetStandardBrowser(Fl_Widget *widget, void *data)
{
  MagicUrlHelper* magicUrlHelper = new MagicUrlHelper();

  if (magicUrlHelper->isMagicUrlInstalled()) {
    magicUrlHelper->setStandardBrowser();
    if (magicUrlHelper->isStandardBrowser() && widget->active()) {
      widget->deactivate();
    }
  }
  delete magicUrlHelper;
}

void OptionsDialog::handleCancel(Fl_Widget *widget, void *data)
{
  OptionsDialog *dialog = (OptionsDialog*)data;

  dialog->hide();
}


void OptionsDialog::handleOK(Fl_Widget *widget, void *data)
{
  OptionsDialog *dialog = (OptionsDialog*)data;

  dialog->hide();

  dialog->storeOptions();
}
