/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "HeadlessThemeGTK.h"

#include "mozilla/StaticPrefs.h"
#include "nsStyleConsts.h"
#include "nsIFrame.h"

namespace mozilla {
namespace widget {

NS_IMPL_ISUPPORTS_INHERITED(HeadlessThemeGTK, nsNativeTheme, nsITheme)

NS_IMETHODIMP
HeadlessThemeGTK::DrawWidgetBackground(gfxContext* aContext, nsIFrame* aFrame,
                                       StyleAppearance aAppearance,
                                       const nsRect& aRect,
                                       const nsRect& aDirtyRect) {
  return NS_OK;
}

LayoutDeviceIntMargin HeadlessThemeGTK::GetWidgetBorder(
    nsDeviceContext* aContext, nsIFrame* aFrame, StyleAppearance aAppearance) {
  if (aAppearance == StyleAppearance::MenulistButton &&
      StaticPrefs::layout_css_webkit_appearance_enabled()) {
    aAppearance = StyleAppearance::Menulist;
  }

  LayoutDeviceIntMargin result;
  // The following values are generated from the Ubuntu GTK theme.
  switch (aAppearance) {
    case StyleAppearance::Button:
    case StyleAppearance::Toolbarbutton:
      result.top = 6;
      result.right = 7;
      result.bottom = 6;
      result.left = 7;
      break;
    case StyleAppearance::FocusOutline:
    case StyleAppearance::MenulistTextfield:
    case StyleAppearance::NumberInput:
    case StyleAppearance::Textfield:
      result.top = 5;
      result.right = 7;
      result.bottom = 5;
      result.left = 7;
      break;
    case StyleAppearance::Statusbarpanel:
    case StyleAppearance::Resizerpanel:
    case StyleAppearance::Listbox:
    case StyleAppearance::Treeview:
    case StyleAppearance::Treeheadersortarrow:
    case StyleAppearance::ProgressBar:
    case StyleAppearance::ProgressbarVertical:
    case StyleAppearance::SpinnerUpbutton:
    case StyleAppearance::SpinnerDownbutton:
    case StyleAppearance::SpinnerTextfield:
    case StyleAppearance::Textarea:
    case StyleAppearance::Menupopup:
    case StyleAppearance::MozGtkInfoBar:
      result.top = 1;
      result.right = 1;
      result.bottom = 1;
      result.left = 1;
      break;
    case StyleAppearance::Treeheadercell:
      result.top = 5;
      result.right = 7;
      result.bottom = 6;
      result.left = 6;
      break;
    case StyleAppearance::Tab:
      result.top = 4;
      result.right = 7;
      result.bottom = 2;
      result.left = 7;
      break;
    case StyleAppearance::Tooltip:
      result.top = 6;
      result.right = 6;
      result.bottom = 6;
      result.left = 6;
      break;
    case StyleAppearance::Menulist:
      result.top = 6;
      result.right = 22;
      result.bottom = 6;
      result.left = 7;
      break;
    case StyleAppearance::MenulistButton:
    case StyleAppearance::MozMenulistButton:
      result.top = 1;
      result.right = 1;
      result.bottom = 1;
      result.left = 0;
      break;
    case StyleAppearance::Menuitem:
    case StyleAppearance::Checkmenuitem:
    case StyleAppearance::Radiomenuitem:
      if (IsRegularMenuItem(aFrame)) {
        break;
      }
      result.top = 3;
      result.right = 5;
      result.bottom = 3;
      result.left = 5;
      break;
    default:
      break;
  }
  return result;
}

bool HeadlessThemeGTK::GetWidgetPadding(nsDeviceContext* aContext,
                                        nsIFrame* aFrame,
                                        StyleAppearance aAppearance,
                                        LayoutDeviceIntMargin* aResult) {
  if (aAppearance == StyleAppearance::MenulistButton &&
      StaticPrefs::layout_css_webkit_appearance_enabled()) {
    aAppearance = StyleAppearance::Menulist;
  }

  // The following values are generated from the Ubuntu GTK theme.
  switch (aAppearance) {
    case StyleAppearance::Radio:
    case StyleAppearance::Checkbox:
    case StyleAppearance::Toolbarbutton:
    case StyleAppearance::Dualbutton:
    case StyleAppearance::ToolbarbuttonDropdown:
    case StyleAppearance::ButtonArrowUp:
    case StyleAppearance::ButtonArrowDown:
    case StyleAppearance::ButtonArrowNext:
    case StyleAppearance::ButtonArrowPrevious:
    case StyleAppearance::TabScrollArrowBack:
    case StyleAppearance::TabScrollArrowForward:
    case StyleAppearance::MenulistButton:
    case StyleAppearance::MozMenulistButton:
    case StyleAppearance::RangeThumb:
    case StyleAppearance::ButtonFocus:
      aResult->top = 0;
      aResult->right = 0;
      aResult->bottom = 0;
      aResult->left = 0;
      return true;
    case StyleAppearance::Menuitem:
    case StyleAppearance::Checkmenuitem:
    case StyleAppearance::Radiomenuitem:
      if (!IsRegularMenuItem(aFrame)) {
        return false;
      }
      aResult->top = 3;
      aResult->right = 5;
      aResult->bottom = 3;
      aResult->left = 5;
      return true;
    default:
      break;
  }
  return false;
}

NS_IMETHODIMP
HeadlessThemeGTK::GetMinimumWidgetSize(nsPresContext* aPresContext,
                                       nsIFrame* aFrame,
                                       StyleAppearance aAppearance,
                                       LayoutDeviceIntSize* aResult,
                                       bool* aIsOverridable) {
  aResult->width = aResult->height = 0;
  *aIsOverridable = true;

  if (aAppearance == StyleAppearance::MenulistButton &&
      StaticPrefs::layout_css_webkit_appearance_enabled()) {
    aAppearance = StyleAppearance::Menulist;
  }

  // The following values are generated from the Ubuntu GTK theme.
  switch (aAppearance) {
    case StyleAppearance::Splitter:
      if (IsHorizontal(aFrame)) {
        aResult->width = 6;
        aResult->height = 0;
      } else {
        aResult->width = 0;
        aResult->height = 6;
      }
      *aIsOverridable = false;
      break;
    case StyleAppearance::Button:
    case StyleAppearance::Toolbarbutton:
      aResult->width = 14;
      aResult->height = 12;
      break;
    case StyleAppearance::Radio:
    case StyleAppearance::Checkbox:
      aResult->width = 18;
      aResult->height = 18;
      break;
    case StyleAppearance::ToolbarbuttonDropdown:
    case StyleAppearance::ButtonArrowUp:
    case StyleAppearance::ButtonArrowDown:
    case StyleAppearance::ButtonArrowNext:
    case StyleAppearance::ButtonArrowPrevious:
    case StyleAppearance::Resizer:
      aResult->width = 15;
      aResult->height = 15;
      *aIsOverridable = false;
      break;
    case StyleAppearance::Separator:
      aResult->width = 12;
      aResult->height = 0;
      break;
    case StyleAppearance::Treetwisty:
    case StyleAppearance::Treetwistyopen:
      aResult->width = 8;
      aResult->height = 8;
      *aIsOverridable = false;
      break;
    case StyleAppearance::Treeheadercell:
      aResult->width = 13;
      aResult->height = 11;
      break;
    case StyleAppearance::Treeheadersortarrow:
    case StyleAppearance::SpinnerUpbutton:
    case StyleAppearance::SpinnerDownbutton:
      aResult->width = 14;
      aResult->height = 13;
      break;
    case StyleAppearance::TabScrollArrowBack:
    case StyleAppearance::TabScrollArrowForward:
      aResult->width = 16;
      aResult->height = 16;
      *aIsOverridable = false;
      break;
    case StyleAppearance::InnerSpinButton:
    case StyleAppearance::Spinner:
      aResult->width = 14;
      aResult->height = 26;
      break;
    case StyleAppearance::MenulistTextfield:
    case StyleAppearance::NumberInput:
    case StyleAppearance::Textfield:
      aResult->width = 0;
      aResult->height = 12;
      break;
    case StyleAppearance::ScrollbarHorizontal:
      aResult->width = 31;
      aResult->height = 10;
      break;
    case StyleAppearance::ScrollbarVertical:
      aResult->width = 10;
      aResult->height = 31;
      break;
    case StyleAppearance::ScrollbarbuttonUp:
    case StyleAppearance::ScrollbarbuttonDown:
      aResult->width = 10;
      aResult->height = 13;
      *aIsOverridable = false;
      break;
    case StyleAppearance::ScrollbarbuttonLeft:
    case StyleAppearance::ScrollbarbuttonRight:
      aResult->width = 13;
      aResult->height = 10;
      *aIsOverridable = false;
      break;
    case StyleAppearance::ScrollbarthumbHorizontal:
      aResult->width = 31;
      aResult->height = 10;
      *aIsOverridable = false;
      break;
    case StyleAppearance::ScrollbarthumbVertical:
      aResult->width = 10;
      aResult->height = 31;
      *aIsOverridable = false;
      break;
    case StyleAppearance::Menulist:
      aResult->width = 44;
      aResult->height = 27;
      break;
    case StyleAppearance::MenulistButton:
    case StyleAppearance::MozMenulistButton:
      aResult->width = 29;
      aResult->height = 28;
      *aIsOverridable = false;
      break;
    case StyleAppearance::ScalethumbHorizontal:
    case StyleAppearance::RangeThumb:
      aResult->width = 14;
      aResult->height = 18;
      *aIsOverridable = false;
      break;
    case StyleAppearance::ScalethumbVertical:
      aResult->width = 18;
      aResult->height = 13;
      *aIsOverridable = false;
      break;
    case StyleAppearance::Menuseparator:
      aResult->width = 0;
      aResult->height = 8;
      *aIsOverridable = false;
      break;
    default:
      break;
  }
  return NS_OK;
}

NS_IMETHODIMP
HeadlessThemeGTK::WidgetStateChanged(nsIFrame* aFrame,
                                     StyleAppearance aAppearance,
                                     nsAtom* aAttribute, bool* aShouldRepaint,
                                     const nsAttrValue* aOldValue) {
  return NS_OK;
}

NS_IMETHODIMP
HeadlessThemeGTK::ThemeChanged() { return NS_OK; }

static bool IsFrameContentNodeInNamespace(nsIFrame* aFrame,
                                          uint32_t aNamespace) {
  nsIContent* content = aFrame ? aFrame->GetContent() : nullptr;
  if (!content) return false;
  return content->IsInNamespace(aNamespace);
}

NS_IMETHODIMP_(bool)
HeadlessThemeGTK::ThemeSupportsWidget(nsPresContext* aPresContext,
                                      nsIFrame* aFrame,
                                      StyleAppearance aAppearance) {
  if (aAppearance == StyleAppearance::MenulistButton &&
      StaticPrefs::layout_css_webkit_appearance_enabled()) {
    aAppearance = StyleAppearance::Menulist;
  }

  switch (aAppearance) {
    case StyleAppearance::Button:
    case StyleAppearance::Radio:
    case StyleAppearance::Checkbox:
    case StyleAppearance::FocusOutline:
    case StyleAppearance::Toolbox:
    case StyleAppearance::Toolbar:
    case StyleAppearance::Toolbarbutton:
    case StyleAppearance::Dualbutton:
    case StyleAppearance::ToolbarbuttonDropdown:
    case StyleAppearance::ButtonArrowUp:
    case StyleAppearance::ButtonArrowDown:
    case StyleAppearance::ButtonArrowNext:
    case StyleAppearance::ButtonArrowPrevious:
    case StyleAppearance::Separator:
    case StyleAppearance::Toolbargripper:
    case StyleAppearance::Splitter:
    case StyleAppearance::Statusbar:
    case StyleAppearance::Statusbarpanel:
    case StyleAppearance::Resizerpanel:
    case StyleAppearance::Resizer:
    case StyleAppearance::Listbox:
    case StyleAppearance::Treeview:
    case StyleAppearance::Treetwisty:
    case StyleAppearance::Treeheadercell:
    case StyleAppearance::Treeheadersortarrow:
    case StyleAppearance::Treetwistyopen:
    case StyleAppearance::ProgressBar:
    case StyleAppearance::Progresschunk:
    case StyleAppearance::ProgressbarVertical:
    case StyleAppearance::Tab:
    case StyleAppearance::Tabpanels:
    case StyleAppearance::TabScrollArrowBack:
    case StyleAppearance::TabScrollArrowForward:
    case StyleAppearance::Tooltip:
    case StyleAppearance::InnerSpinButton:
    case StyleAppearance::Spinner:
    case StyleAppearance::SpinnerUpbutton:
    case StyleAppearance::SpinnerDownbutton:
    case StyleAppearance::SpinnerTextfield:
    case StyleAppearance::NumberInput:
    case StyleAppearance::ScrollbarHorizontal:
    case StyleAppearance::ScrollbarVertical:
    case StyleAppearance::ScrollbarbuttonUp:
    case StyleAppearance::ScrollbarbuttonDown:
    case StyleAppearance::ScrollbarbuttonLeft:
    case StyleAppearance::ScrollbarbuttonRight:
    case StyleAppearance::ScrollbartrackHorizontal:
    case StyleAppearance::ScrollbartrackVertical:
    case StyleAppearance::ScrollbarthumbHorizontal:
    case StyleAppearance::ScrollbarthumbVertical:
    case StyleAppearance::Textfield:
    case StyleAppearance::Textarea:
    case StyleAppearance::Menulist:
    case StyleAppearance::MenulistText:
    case StyleAppearance::MenulistTextfield:
    case StyleAppearance::ScaleHorizontal:
    case StyleAppearance::ScaleVertical:
    case StyleAppearance::ScalethumbHorizontal:
    case StyleAppearance::ScalethumbVertical:
    case StyleAppearance::Range:
    case StyleAppearance::RangeThumb:
    case StyleAppearance::CheckboxContainer:
    case StyleAppearance::RadioContainer:
    case StyleAppearance::CheckboxLabel:
    case StyleAppearance::RadioLabel:
    case StyleAppearance::ButtonFocus:
    case StyleAppearance::Window:
    case StyleAppearance::Dialog:
    case StyleAppearance::Menubar:
    case StyleAppearance::Menupopup:
    case StyleAppearance::Menuitem:
    case StyleAppearance::Checkmenuitem:
    case StyleAppearance::Radiomenuitem:
    case StyleAppearance::Menuseparator:
    case StyleAppearance::Menuarrow:
    case StyleAppearance::MozGtkInfoBar:
      return !IsWidgetStyled(aPresContext, aFrame, aAppearance);
    case StyleAppearance::MenulistButton:
    case StyleAppearance::MozMenulistButton:
      return (!aFrame ||
              IsFrameContentNodeInNamespace(aFrame, kNameSpaceID_XUL)) &&
             !IsWidgetStyled(aPresContext, aFrame, aAppearance);
    default:
      break;
  }
  return false;
}

NS_IMETHODIMP_(bool)
HeadlessThemeGTK::WidgetIsContainer(StyleAppearance aAppearance) {
  if (aAppearance == StyleAppearance::MenulistButton &&
      StaticPrefs::layout_css_webkit_appearance_enabled()) {
    aAppearance = StyleAppearance::Menulist;
  }

  if (aAppearance == StyleAppearance::MenulistButton ||
      aAppearance == StyleAppearance::MozMenulistButton ||
      aAppearance == StyleAppearance::Radio ||
      aAppearance == StyleAppearance::RangeThumb ||
      aAppearance == StyleAppearance::Checkbox ||
      aAppearance == StyleAppearance::TabScrollArrowBack ||
      aAppearance == StyleAppearance::TabScrollArrowForward ||
      aAppearance == StyleAppearance::ButtonArrowUp ||
      aAppearance == StyleAppearance::ButtonArrowDown ||
      aAppearance == StyleAppearance::ButtonArrowNext ||
      aAppearance == StyleAppearance::ButtonArrowPrevious) {
    return false;
  }
  return true;
}

bool HeadlessThemeGTK::ThemeDrawsFocusForWidget(StyleAppearance aAppearance) {
  if (aAppearance == StyleAppearance::Menulist ||
      aAppearance == StyleAppearance::Button ||
      aAppearance == StyleAppearance::Treeheadercell) {
    return true;
  }
  return false;
}

bool HeadlessThemeGTK::ThemeNeedsComboboxDropmarker() { return false; }

}  // namespace widget
}  // namespace mozilla
