void MainWindow::setup() {
  window = new Window;
  window->setObjectName("main-window");
  window->setWindowTitle(BSNES_TITLE);

  system = window->menuBar()->addMenu("&System");
    system_load = system->addAction("&Load Cartridge ...");
    system->addSeparator();
    system_power = system->addMenu("&Power");
      system_power_on = system_power->addAction("On");
      system_power_on->setCheckable(true);
      system_power_off = system_power->addAction("Off");
      system_power_off->setCheckable(true);
    system_reset = system->addAction("&Reset");
    system_irq = system->addAction("&Irq");
    system->addSeparator();
    system_port1 = system->addMenu("Controller Port 1");
      system_port1_none = system_port1->addAction("&None");
      system_port1_none->setCheckable(true);
      system_port1_joypad = system_port1->addAction("&Joypad");
      system_port1_joypad->setCheckable(true);
      system_port1_multitap = system_port1->addAction("&Multitap");
      system_port1_multitap->setCheckable(true);
      system_port1_mouse = system_port1->addAction("M&ouse");
      system_port1_mouse->setCheckable(true);
    system_port2 = system->addMenu("Controller Port 2");
      system_port2_none = system_port2->addAction("&None");
      system_port2_none->setCheckable(true);
      system_port2_joypad = system_port2->addAction("&Joypad");
      system_port2_joypad->setCheckable(true);
      system_port2_multitap = system_port2->addAction("&Multitap");
      system_port2_multitap->setCheckable(true);
      system_port2_mouse = system_port2->addAction("M&ouse");
      system_port2_mouse->setCheckable(true);
      system_port2_superscope = system_port2->addAction("&Super Scope");
      system_port2_superscope->setCheckable(true);
      system_port2_justifier = system_port2->addAction("&Justifier");
      system_port2_justifier->setCheckable(true);
      system_port2_justifiers = system_port2->addAction("&Two Justifiers");
      system_port2_justifiers->setCheckable(true);
    system->addSeparator();
    system_exit = system->addAction("E&xit");
    system_exit->setMenuRole(QAction::QuitRole);

  settings = window->menuBar()->addMenu("S&ettings");
    settings_videoMode = settings->addMenu("&Video Mode");
      settings_videoMode_1x = settings_videoMode->addAction("Scale &1x");
      settings_videoMode_1x->setCheckable(true);
      settings_videoMode_2x = settings_videoMode->addAction("Scale &2x");
      settings_videoMode_2x->setCheckable(true);
      settings_videoMode_3x = settings_videoMode->addAction("Scale &3x");
      settings_videoMode_3x->setCheckable(true);
      settings_videoMode_4x = settings_videoMode->addAction("Scale &4x");
      settings_videoMode_4x->setCheckable(true);
      settings_videoMode_max = settings_videoMode->addAction("Scale &Max");
      settings_videoMode_max->setCheckable(true);
      settings_videoMode_max->setStatusTip("Scale video output to fill as much of the screen as possible");
      settings_videoMode->addSeparator();
      settings_videoMode_correctAspectRatio = settings_videoMode->addAction("&Correct Aspect Ratio");
      settings_videoMode_correctAspectRatio->setStatusTip("Match pixel width-to-height ratio of TV");
      settings_videoMode_correctAspectRatio->setCheckable(true);
      settings_videoMode_fullscreen = settings_videoMode->addAction("&Fullscreen");
      settings_videoMode_fullscreen->setCheckable(true);
      settings_videoMode->addSeparator();
      settings_videoMode_ntsc = settings_videoMode->addAction("&NTSC");
      settings_videoMode_ntsc->setCheckable(true);
      settings_videoMode_ntsc->setStatusTip("Size video output window to match NTSC TV spec");
      settings_videoMode_pal = settings_videoMode->addAction("&PAL");
      settings_videoMode_pal->setCheckable(true);
      settings_videoMode_pal->setStatusTip("Size video output window to match PAL TV spec");
    settings_videoFilter = settings->addMenu("Video &Filter");
      settings_videoFilter_point = settings_videoFilter->addAction("&Point");
      settings_videoFilter_point->setCheckable(true);
      settings_videoFilter_point->setStatusTip("Use pixellated hardware video scaling");
      settings_videoFilter_linear = settings_videoFilter->addAction("&Linear");
      settings_videoFilter_linear->setCheckable(true);
      settings_videoFilter_linear->setStatusTip("Use smoothed hardware video scaling");
      settings_videoFilter->addSeparator();
      settings_videoFilter_none = settings_videoFilter->addAction("&None");
      settings_videoFilter_none->setCheckable(true);
      settings_videoFilter_scanline = settings_videoFilter->addAction("&Scanline");
      settings_videoFilter_scanline->setCheckable(true);
      settings_videoFilter_scale2x = settings_videoFilter->addAction("S&cale2x");
      settings_videoFilter_scale2x->setCheckable(true);
      settings_videoFilter_hq2x = settings_videoFilter->addAction("&HQ2x");
      settings_videoFilter_hq2x->setCheckable(true);
      settings_videoFilter_ntsc = settings_videoFilter->addAction("N&TSC");
      settings_videoFilter_ntsc->setCheckable(true);
    settings->addSeparator();
    settings_muteAudio = settings->addAction("&Mute Audio Output");
    settings_muteAudio->setCheckable(true);
    settings->addSeparator();
    settings_emulationSpeed = settings->addMenu("&Emulation Speed");
      settings_emulationSpeed_slowest = settings_emulationSpeed->addAction("50%");
      settings_emulationSpeed_slowest->setCheckable(true);
      settings_emulationSpeed_slow = settings_emulationSpeed->addAction("75%");
      settings_emulationSpeed_slow->setCheckable(true);
      settings_emulationSpeed_normal = settings_emulationSpeed->addAction("100%");
      settings_emulationSpeed_normal->setCheckable(true);
      settings_emulationSpeed_fast = settings_emulationSpeed->addAction("150%");
      settings_emulationSpeed_fast->setCheckable(true);
      settings_emulationSpeed_fastest = settings_emulationSpeed->addAction("200%");
      settings_emulationSpeed_fastest->setCheckable(true);
      settings_emulationSpeed->addSeparator();
      settings_emulationSpeed_syncVideo = settings_emulationSpeed->addAction("Sync &Video");
      settings_emulationSpeed_syncVideo->setCheckable(true);
      settings_emulationSpeed_syncVideo->setStatusTip("Sync video output to vertical refresh rate");
      settings_emulationSpeed_syncAudio = settings_emulationSpeed->addAction("Sync &Audio");
      settings_emulationSpeed_syncAudio->setCheckable(true);
      settings_emulationSpeed_syncAudio->setStatusTip("Sync audio output to sound card output rate");
    settings_configuration = settings->addAction("&Configuration ...");
    settings_configuration->setMenuRole(QAction::PreferencesRole);

  help = window->menuBar()->addMenu("&Help");
    help_documentation = help->addAction("Documentation ...");
    help_license = help->addAction("License ...");
    help->addSeparator();
    help_about = help->addAction("&About ...");
    help_about->setMenuRole(QAction::AboutRole);

  canvasContainer = new CanvasObject;
  canvasContainer->setAcceptDrops(true); {
    canvasContainer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    canvasContainer->setObjectName("backdrop");

    canvasLayout = new QVBoxLayout; {
      canvasLayout->setMargin(0);
      canvasLayout->setAlignment(Qt::AlignCenter);

      canvas = new CanvasWidget;
      canvas->setAcceptDrops(true);
      canvas->setAttribute(Qt::WA_PaintOnScreen, true);  //disable Qt painting on focus / resize

      QPalette palette;
      palette.setColor(QPalette::Window, QColor(0, 0, 0));
      canvas->setPalette(palette);
      canvas->setAutoFillBackground(true);
    }
    canvasLayout->addWidget(canvas);
  }
  canvasContainer->setLayout(canvasLayout);

  window->statusBar()->showMessage("");
  systemState = new QLabel;
  window->statusBar()->addPermanentWidget(systemState);

  window->setCentralWidget(canvasContainer);

  //slots
  connect(system_load, SIGNAL(triggered()), this, SLOT(loadCartridge()));
  connect(system_power_on, SIGNAL(triggered()), this, SLOT(powerOn()));
  connect(system_power_off, SIGNAL(triggered()), this, SLOT(powerOff()));
  connect(system_reset, SIGNAL(triggered()), this, SLOT(reset()));
  connect(system_irq, SIGNAL(triggered()), this, SLOT(irq()));
  connect(system_port1_none, SIGNAL(triggered()), this, SLOT(setPort1None()));
  connect(system_port1_joypad, SIGNAL(triggered()), this, SLOT(setPort1Joypad()));
  connect(system_port1_multitap, SIGNAL(triggered()), this, SLOT(setPort1Multitap()));
  connect(system_port1_mouse, SIGNAL(triggered()), this, SLOT(setPort1Mouse()));
  connect(system_port2_none, SIGNAL(triggered()), this, SLOT(setPort2None()));
  connect(system_port2_joypad, SIGNAL(triggered()), this, SLOT(setPort2Joypad()));
  connect(system_port2_multitap, SIGNAL(triggered()), this, SLOT(setPort2Multitap()));
  connect(system_port2_mouse, SIGNAL(triggered()), this, SLOT(setPort2Mouse()));
  connect(system_port2_superscope, SIGNAL(triggered()), this, SLOT(setPort2SuperScope()));
  connect(system_port2_justifier, SIGNAL(triggered()), this, SLOT(setPort2Justifier()));
  connect(system_port2_justifiers, SIGNAL(triggered()), this, SLOT(setPort2Justifiers()));
  connect(system_exit, SIGNAL(triggered()), this, SLOT(quit()));
  connect(settings_videoMode_1x, SIGNAL(triggered()), this, SLOT(setVideoMode1x()));
  connect(settings_videoMode_2x, SIGNAL(triggered()), this, SLOT(setVideoMode2x()));
  connect(settings_videoMode_3x, SIGNAL(triggered()), this, SLOT(setVideoMode3x()));
  connect(settings_videoMode_4x, SIGNAL(triggered()), this, SLOT(setVideoMode4x()));
  connect(settings_videoMode_max, SIGNAL(triggered()), this, SLOT(setVideoModeMax()));
  connect(settings_videoMode_correctAspectRatio, SIGNAL(triggered()), this, SLOT(toggleAspectCorrection()));
  connect(settings_videoMode_fullscreen, SIGNAL(triggered()), this, SLOT(toggleFullscreen()));
  connect(settings_videoMode_ntsc, SIGNAL(triggered()), this, SLOT(setVideoNtsc()));
  connect(settings_videoMode_pal, SIGNAL(triggered()), this, SLOT(setVideoPal()));
  connect(settings_videoFilter_point, SIGNAL(triggered()), this, SLOT(setPointFilter()));
  connect(settings_videoFilter_linear, SIGNAL(triggered()), this, SLOT(setLinearFilter()));
  connect(settings_videoFilter_none, SIGNAL(triggered()), this, SLOT(setNoFilter()));
  connect(settings_videoFilter_scanline, SIGNAL(triggered()), this, SLOT(setScanlineFilter()));
  connect(settings_videoFilter_scale2x, SIGNAL(triggered()), this, SLOT(setScale2xFilter()));
  connect(settings_videoFilter_hq2x, SIGNAL(triggered()), this, SLOT(setHq2xFilter()));
  connect(settings_videoFilter_ntsc, SIGNAL(triggered()), this, SLOT(setNtscFilter()));
  connect(settings_muteAudio, SIGNAL(triggered()), this, SLOT(muteAudio()));
  connect(settings_emulationSpeed_slowest, SIGNAL(triggered()), this, SLOT(setSpeedSlowest()));
  connect(settings_emulationSpeed_slow, SIGNAL(triggered()), this, SLOT(setSpeedSlow()));
  connect(settings_emulationSpeed_normal, SIGNAL(triggered()), this, SLOT(setSpeedNormal()));
  connect(settings_emulationSpeed_fast, SIGNAL(triggered()), this, SLOT(setSpeedFast()));
  connect(settings_emulationSpeed_fastest, SIGNAL(triggered()), this, SLOT(setSpeedFastest()));
  connect(settings_emulationSpeed_syncVideo, SIGNAL(triggered()), this, SLOT(syncVideo()));
  connect(settings_emulationSpeed_syncAudio, SIGNAL(triggered()), this, SLOT(syncAudio()));
  connect(settings_configuration, SIGNAL(triggered()), this, SLOT(showConfigWindow()));
  connect(help_documentation, SIGNAL(triggered()), this, SLOT(showDocumentation()));
  connect(help_license, SIGNAL(triggered()), this, SLOT(showLicense()));
  connect(help_about, SIGNAL(triggered()), this, SLOT(showAbout()));

  syncUi();
}

void MainWindow::syncUi() {
  system_power->setEnabled(cartridge.loaded());
  system_power_on->setChecked (application.power == true);
  system_power_off->setChecked(application.power == false);
  system_reset->setEnabled(cartridge.loaded() && application.power);

  system_port1_none->setChecked      (snes.config.controller_port1 == SNES::Input::DeviceNone);
  system_port1_joypad->setChecked    (snes.config.controller_port1 == SNES::Input::DeviceJoypad);
  system_port1_multitap->setChecked  (snes.config.controller_port1 == SNES::Input::DeviceMultitap);
  system_port1_mouse->setChecked     (snes.config.controller_port1 == SNES::Input::DeviceMouse);
  system_port2_none->setChecked      (snes.config.controller_port2 == SNES::Input::DeviceNone);
  system_port2_joypad->setChecked    (snes.config.controller_port2 == SNES::Input::DeviceJoypad);
  system_port2_multitap->setChecked  (snes.config.controller_port2 == SNES::Input::DeviceMultitap);
  system_port2_mouse->setChecked     (snes.config.controller_port2 == SNES::Input::DeviceMouse);
  system_port2_superscope->setChecked(snes.config.controller_port2 == SNES::Input::DeviceSuperScope);
  system_port2_justifier->setChecked (snes.config.controller_port2 == SNES::Input::DeviceJustifier);
  system_port2_justifiers->setChecked(snes.config.controller_port2 == SNES::Input::DeviceJustifiers);

  settings_videoMode_1x->setChecked (config.video.context->multiplier == 1);
  settings_videoMode_2x->setChecked (config.video.context->multiplier == 2);
  settings_videoMode_3x->setChecked (config.video.context->multiplier == 3);
  settings_videoMode_4x->setChecked (config.video.context->multiplier == 4);
  settings_videoMode_max->setChecked(config.video.context->multiplier >= 5);

  settings_videoMode_correctAspectRatio->setChecked(config.video.context->correctAspectRatio);
  settings_videoMode_fullscreen->setChecked(config.video.isFullscreen);
  settings_videoMode_ntsc->setChecked(config.video.context->region == 0);
  settings_videoMode_pal->setChecked (config.video.context->region == 1);

  settings_videoFilter_point->setChecked   (config.video.context->hwFilter == 0);
  settings_videoFilter_linear->setChecked  (config.video.context->hwFilter == 1);
  settings_videoFilter_none->setChecked    (config.video.context->swFilter == 0);
  settings_videoFilter_scanline->setChecked(config.video.context->swFilter == 1);
  settings_videoFilter_scale2x->setChecked (config.video.context->swFilter == 2);
  settings_videoFilter_hq2x->setChecked    (config.video.context->swFilter == 3);
  settings_videoFilter_ntsc->setChecked    (config.video.context->swFilter == 4);

  settings_muteAudio->setChecked(config.audio.mute);

  settings_emulationSpeed_slowest->setChecked(config.system.speed == 0);
  settings_emulationSpeed_slow->setChecked   (config.system.speed == 1);
  settings_emulationSpeed_normal->setChecked (config.system.speed == 2);
  settings_emulationSpeed_fast->setChecked   (config.system.speed == 3);
  settings_emulationSpeed_fastest->setChecked(config.system.speed == 4);

  settings_emulationSpeed_syncVideo->setChecked(config.video.synchronize);
  settings_emulationSpeed_syncAudio->setChecked(config.audio.synchronize);
}

void MainWindow::loadCartridge() {
  string filename = utility.selectCartridge();
  if(filename.length() > 0) utility.loadCartridge(filename);
}

void MainWindow::powerOn()  { utility.modifySystemState(Utility::PowerOn); }
void MainWindow::powerOff() { utility.modifySystemState(Utility::PowerOff); }
void MainWindow::reset()    { utility.modifySystemState(Utility::Reset); }
void MainWindow::irq()    { utility.modifySystemState(Utility::Irq); }

void MainWindow::setPort1None()       { snes.config.controller_port1 = SNES::Input::DeviceNone;       utility.updateControllers(); syncUi(); }
void MainWindow::setPort1Joypad()     { snes.config.controller_port1 = SNES::Input::DeviceJoypad;     utility.updateControllers(); syncUi(); }
void MainWindow::setPort1Multitap()   { snes.config.controller_port1 = SNES::Input::DeviceMultitap;   utility.updateControllers(); syncUi(); }
void MainWindow::setPort1Mouse()      { snes.config.controller_port1 = SNES::Input::DeviceMouse;      utility.updateControllers(); syncUi(); }
void MainWindow::setPort2None()       { snes.config.controller_port2 = SNES::Input::DeviceNone;       utility.updateControllers(); syncUi(); }
void MainWindow::setPort2Joypad()     { snes.config.controller_port2 = SNES::Input::DeviceJoypad;     utility.updateControllers(); syncUi(); }
void MainWindow::setPort2Multitap()   { snes.config.controller_port2 = SNES::Input::DeviceMultitap;   utility.updateControllers(); syncUi(); }
void MainWindow::setPort2Mouse()      { snes.config.controller_port2 = SNES::Input::DeviceMouse;      utility.updateControllers(); syncUi(); }
void MainWindow::setPort2SuperScope() { snes.config.controller_port2 = SNES::Input::DeviceSuperScope; utility.updateControllers(); syncUi(); }
void MainWindow::setPort2Justifier()  { snes.config.controller_port2 = SNES::Input::DeviceJustifier;  utility.updateControllers(); syncUi(); }
void MainWindow::setPort2Justifiers() { snes.config.controller_port2 = SNES::Input::DeviceJustifiers; utility.updateControllers(); syncUi(); }

void MainWindow::quit() {
  application.terminate = true;
}

void MainWindow::setVideoMode1x()  { config.video.context->multiplier = 1; utility.resizeMainWindow(); syncUi(); }
void MainWindow::setVideoMode2x()  { config.video.context->multiplier = 2; utility.resizeMainWindow(); syncUi(); }
void MainWindow::setVideoMode3x()  { config.video.context->multiplier = 3; utility.resizeMainWindow(); syncUi(); }
void MainWindow::setVideoMode4x()  { config.video.context->multiplier = 4; utility.resizeMainWindow(); syncUi(); }
void MainWindow::setVideoModeMax() { config.video.context->multiplier = 9; utility.resizeMainWindow(); syncUi(); }

void MainWindow::toggleAspectCorrection() {
  config.video.context->correctAspectRatio = settings_videoMode_correctAspectRatio->isChecked();
  utility.resizeMainWindow();
}

void MainWindow::toggleFullscreen() { config.video.isFullscreen = settings_videoMode_fullscreen->isChecked(); utility.updateFullscreenState(); syncUi(); }

void MainWindow::setVideoNtsc() { config.video.context->region = 0; utility.updateVideoMode(); utility.resizeMainWindow(); syncUi(); }
void MainWindow::setVideoPal()  { config.video.context->region = 1; utility.updateVideoMode(); utility.resizeMainWindow(); syncUi(); }

void MainWindow::setPointFilter()    { config.video.context->hwFilter = 0; utility.updateHardwareFilter(); syncUi(); }
void MainWindow::setLinearFilter()   { config.video.context->hwFilter = 1; utility.updateHardwareFilter(); syncUi(); }
void MainWindow::setNoFilter()       { config.video.context->swFilter = 0; utility.updateSoftwareFilter(); syncUi(); }
void MainWindow::setScanlineFilter() { config.video.context->swFilter = 1; utility.updateSoftwareFilter(); syncUi(); }
void MainWindow::setScale2xFilter()  { config.video.context->swFilter = 2; utility.updateSoftwareFilter(); syncUi(); }
void MainWindow::setHq2xFilter()     { config.video.context->swFilter = 3; utility.updateSoftwareFilter(); syncUi(); }
void MainWindow::setNtscFilter()     { config.video.context->swFilter = 4; utility.updateSoftwareFilter(); syncUi(); }

void MainWindow::muteAudio() { config.audio.mute = settings_muteAudio->isChecked(); }

void MainWindow::setSpeedSlowest() { config.system.speed = 0; utility.updateEmulationSpeed(); syncUi(); }
void MainWindow::setSpeedSlow()    { config.system.speed = 1; utility.updateEmulationSpeed(); syncUi(); }
void MainWindow::setSpeedNormal()  { config.system.speed = 2; utility.updateEmulationSpeed(); syncUi(); }
void MainWindow::setSpeedFast()    { config.system.speed = 3; utility.updateEmulationSpeed(); syncUi(); }
void MainWindow::setSpeedFastest() { config.system.speed = 4; utility.updateEmulationSpeed(); syncUi(); }

void MainWindow::syncVideo() { config.video.synchronize = settings_emulationSpeed_syncVideo->isChecked(); utility.updateAvSync(); }
void MainWindow::syncAudio() { config.audio.synchronize = settings_emulationSpeed_syncAudio->isChecked(); utility.updateAvSync(); }

void MainWindow::showConfigWindow() {
  utility.showCentered(winSettings->window);
}

void MainWindow::showDocumentation()  {
  QFile file(":/documentation.html");
  if(file.open(QIODevice::ReadOnly | QIODevice::Text)) {
    winHtmlViewer->show("Usage Documentation", file.readAll().constData());
    file.close();
  }
}

void MainWindow::showLicense() {
  QFile file(":/license.html");
  if(file.open(QIODevice::ReadOnly | QIODevice::Text)) {
    winHtmlViewer->show("License Agreement", file.readAll().constData());
    file.close();
  }
}
void MainWindow::showAbout() {
  utility.showCentered(winAbout->window);
}

void MainWindow::Window::closeEvent(QCloseEvent*) {
  winMain->quit();
}

//============
//CanvasObject
//============
//implement drag-and-drop support:
//drag cartridge image onto main window canvas area to load

void CanvasObject::dragEnterEvent(QDragEnterEvent *event) {
  if(event->mimeData()->hasUrls()) {
    //do not accept multiple files at once
    if(event->mimeData()->urls().count() == 1) event->acceptProposedAction();
  }
}

void CanvasObject::dropEvent(QDropEvent *event) {
  if(event->mimeData()->hasUrls()) {
    QList<QUrl> list = event->mimeData()->urls();
    if(list.count() == 1) utility.loadCartridge(list.at(0).toLocalFile().toUtf8().constData());
  }
}

//===========
//CanvasWidget
//============
//custom video render and mouse capture functionality

QPaintEngine* CanvasWidget::paintEngine() const {
  if(cartridge.loaded()) return 0;
  return QWidget::paintEngine();
}

void CanvasWidget::mouseReleaseEvent(QMouseEvent *event) {
  //acquire exclusive mode access to mouse when video output widget is clicked
  //(will only acquire if cart is loaded, and mouse / lightgun is in use.)
  utility.acquireMouse();
}

void CanvasWidget::paintEvent(QPaintEvent *event) {
  event->ignore();
}
