// -*- mode: c++; c-file-style: "linux"; c-basic-offset: 2; indent-tabs-mode: nil -*-
//
//  Copyright (C) 2004-2015 Andrej Vodopivec <andrej.vodopivec@gmail.com>
//
//  This program 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 program 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 program; if not, write to the Free Software
//  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
//  SPDX-License-Identifier: GPL-2.0+

#ifndef CONFIGURATION_H
#define CONFIGURATION_H

#include "precomp.h"
#include <wx/wx.h>
#include <wx/config.h>
#include <wx/display.h>
#include <wx/fontenum.h>
#include <wx/hashmap.h>
#include "dialogs/LoggingMessageDialog.h"
#include "cells/TextStyle.h"
#include "FontRenderabilityCache.h"
#include "MaximaSessionInfo.h"
#include "RenderContext.h"
#include "Styles.h"
#include <cstdint>
#include <functional>
#include <memory>
#include <mutex>
#include <unordered_map>
#include <random>
#include <list>
#include <variant>
#include <vector>
#include <algorithm>
#include <atomic>
#include <chrono>
#include <wx/wupdlock.h>
#include <list>

#define MC_LINE_SKIP Scale_Px(2)
#define MC_TEXT_PADDING Scale_Px(1)

#define PAREN_OPEN_TOP_UNICODE     "\u239b"
#define PAREN_OPEN_EXTEND_UNICODE  "\u239c"
#define PAREN_OPEN_BOTTOM_UNICODE  "\u239d"
#define PAREN_CLOSE_TOP_UNICODE    "\u239e"
#define PAREN_CLOSE_EXTEND_UNICODE "\u239f"
#define PAREN_CLOSE_BOTTOM_UNICODE "\u23a0"
#define SUM_SIGN "\u2211"
#define PROD_SIGN "\u220F"
//! How much to make the font smaller for the sum index and limit?
#define SUM_DEC 2

//! The width of the horizontally-drawn cursor
#define MC_HCARET_WIDTH 25

#define MC_EXP_INDENT static_cast<wxCoord>(Scale_Px(2))
static constexpr AFontSize MC_MIN_SIZE{ 6.0f };
static constexpr AFontSize MC_MAX_SIZE{ 48.0f };

// The minimal and maximal label width
#define LABELWIDTH_MIN 3
#define LABELWIDTH_MAX 10
class Cell;
class DocumentCellPointers;
class ViewCellPointers;
class GroupCell;
template<class T> class wxScrolled;
typedef wxScrolled<wxWindow> wxScrolledCanvas;

/*! The configuration storage for the current worksheet.

  Caching the information here means we don't need to search for the configuration
  value's name every time we need the information: Reading configuration values from
  the system's configuration storage can be quite time consuming, especially on a
  MSW with a long registry.

  In order to make all cells temporary listen to a different set of configuration
  than the default one all that has to be done is to create a new configuration
  object that contains hold the right settings for printing/export as bitmap or
  similar: Configuration::Get() will always return the last Configuration that was
  created and therefore as long as the new configuration object exist will return
  a pointer to this object if configuration is needed.
*/
class Configuration
{
public:
  struct PerformanceStats {
    std::atomic<long> manualAnchorsFromBuiltin{0};
    std::atomic<long> manualAnchorsFromCache{0};
    std::atomic<long> manualAnchorsCompiled{0};
    std::atomic<long> maximaProcessesSpawned{0};
    std::atomic<long> fontCacheHits{0};
    std::atomic<long> fontCacheMisses{0};
    std::atomic<long> recalculationNeeded_FontInvalid{0};
    std::atomic<long> recalculationNeeded_SizeInvalid{0};
    std::atomic<long> recalculationNeeded_FontMismatch{0};
    std::atomic<long> recalculationNeeded_ConfigChanged{0};
    std::atomic<long> recalculationNeeded_CellsAppended{0};
    std::atomic<long> recalculationNeeded_EditorDirty{0};
    std::atomic<long> cellsConvertedToLinear{0};
    std::atomic<long> cellsConvertedTo2D{0};
    /*! How many cells were drawn at a different font size than they were laid out with

      Nonzero means a cell was re-measured behind its owner's back (e.g. a
      lazy GetWidth() repair or a draw-list recalculation at the top-level
      font size) after the surrounding cells had already cached extents based
      on the owner-dictated size: the visible symptom is text in 2D fractions
      shown at full size and parentheses narrower than their content.
    */
    std::atomic<long> cellsDrawnAtWrongFontSize{0};
    //! How often the worksheet was repainted (Worksheet::OnPaint ran)
    std::atomic<long> worksheetRepaints{0};
    /*! How many of those repaints covered (nearly) the whole visible worksheet

      Full repaints re-render every visible cell's text, so they are the
      expensive kind: this counter rising while the user merely moves the
      mouse or a cursor blinks reproduces the "constant full redraws"
      performance bug class (e.g. the composited GTK overlay scrollbar).
    */
    std::atomic<long> worksheetFullRepaints{0};
    void Report() const;
  };
  static PerformanceStats g_stats;

  enum maximaHelpFormat{
    maxima = 0,
    frontend = 1,
    browser = 2
  };
  //! The export formats we support for HTML equations
  enum htmlExportFormat
  {
    mathJaX_TeX = 0,
    bitmap = 1,
    mathML_mathJaX = 2,
    svg = 3,
    html_export_invalidChoice
  };

  enum showLabels : int8_t
  {
    labels_automatic = 0,
    labels_prefer_user = 1,
    labels_useronly = 2,
    labels_none = 3,
    labels_invalidSelection
  };

  enum drawMode
  {
    ascii,              //!< Use ascii characters only
    handdrawn           //!< A  parenthesis sign that was created using draw commands
  };

  enum InitOpt
  {
    none,
    temporary          //!< This configuration is temporary and shouldn't redetect Maxima etc.
  };

  enum mathDisplayMode
  {
    display_2d,
    display_2dASCII,
    display_2dUNICODE,
    display_1dASCII
  };

  typedef std::unordered_map <wxString, bool, wxStringHash> StringBoolHash;
  //! Coincides name with a operator known to maxima?
  bool IsOperator(wxString name) const {return m_maximaSession.IsOperator(name);}
  //! Register name as an operator known to maxima.
  void AddMaximaOperator(const wxString &name){m_maximaSession.AddOperator(name);}
  const wxEnvVariableHashMap& MaximaEnvVars() const {return m_maximaEnvVars;}
  //! Forget all custom environment variables for the maxima process.
  void ClearMaximaEnvVars(){m_maximaEnvVars.clear();}
  //! Set a custom environment variable for the maxima process.
  void SetMaximaEnvVar(const wxString &name, const wxString &value){m_maximaEnvVars[name] = value;}

  //! Does maxima output 1D, 2D or 2D ASCII equations?
  mathDisplayMode DisplayMode() const {return m_displayMode;}
  //! Tell the config if maxima outputs 1D, 2D or 2D ASCII equations, currently
  void DisplayMode(mathDisplayMode mode ) {m_displayMode = mode;}

  //! Set maxima's working directory
  void SetWorkingDirectory(wxString dir)
    { m_maximaSession.SetWorkingDirectory(std::move(dir)); }

  //! Get maxima's working directory
  wxString GetWorkingDirectory() const
    { return m_maximaSession.GetWorkingDirectory(); }

  //! Read the config from the wxConfig object
  void ReadConfig();

  /*! The constructor

    \param dc The drawing context that is to be used for drawing objects
  */
  explicit Configuration(wxDC *dc = {}, InitOpt options = none);
  Configuration(const Configuration &o);

  //! Reset the whole configuration to its default values
  void ResetAllToDefaults();

  //! Set the drawing context that is currently active
  void SetRecalcContext(wxDC &dc)
    {
      m_renderContext.SetRecalcDC(&dc);
    }
  void UnsetContext() {m_renderContext.SetRecalcDC(NULL);}

  //! Set the brush to be used for the worksheet background
  void SetBackgroundBrush(const wxBrush &brush);
  //! Use a typewriter font in wizard entry fields that can contain maxima commands?
  bool FixedFontInTextControls() const {return m_fixedFontTC;}
  //! Use a typewriter font in wizard entry fields that can contain maxima commands?
  void FixedFontInTextControls(bool fixed) {m_fixedFontTC = fixed;}
  //! Get the brush to be used for the worksheet background
  wxBrush GetBackgroundBrush() const {return m_renderContext.GetBackgroundBrush();}
  //! Get the brush to be used for worksheet objects that provide a mouse-over tooltip
  wxBrush GetTooltipBrush() const {return m_tooltipBrush;}

  virtual ~Configuration();

  /*! Where to store the configuration.

    Empty means: "Use the default location", which on MS Windows is somewhere in the registry.
  */
  static wxString m_configfileLocation_override;

  using EscCodeContainer = std::unordered_map<wxString, wxString, wxStringHash>;
  using EscCodeIterator = EscCodeContainer::const_iterator;

  //! Retrieve a symbol for the escape codes that can be typed after hitting the Escape key in the worksheet.
  static const wxString &GetEscCode(const wxString &key);
  //! Iterators over the escape code list
  static EscCodeIterator EscCodesBegin();
  //! Iterators over the escape code list
  static EscCodeIterator EscCodesEnd();

  //! The lower limit we allow for the zoom factor
  static double GetMinZoomFactor()
    { return 0.1; }

  //! The upper limit we allow for the zoom factor
  static double GetMaxZoomFactor()
    { return 32.0; }

  /*! Extra space to leave between two equations in output cells.

    Extra space between equations is useful if we don't display labels that show
    which line begins a new equation and which line merely continues a multi-line
    equation.
  */
  double GetInterEquationSkip() const
    {
      if (ShowAutomaticLabels())
        return 0;
      else
        return GetZoomFactor() * m_styleStore[TS_MATH].GetFontSize() / 2;
    }

  //! The width we allocate for our cell brackets
  wxCoord GetCellBracketWidth() const
    {
      return static_cast<wxCoord>(GetZoomFactor() * 16);
    }

  //! Hide brackets that are not under the pointer?
  bool HideBrackets() const
    { return m_hideBrackets; }

  //! Define if we want to hide brackets that are not under the pointer.
  void HideBrackets(bool hide){m_hideBrackets = hide;}

  /*! Use GTK's overlay scrollbars on the worksheet?

    Off by default: the fading overlay indicator is a composited window, and
    while its animation runs (after every pointer or wheel event) GTK
    repaints the whole worksheet on every animation frame - see
    Worksheet::ApplyOverlayScrollbarsSetting(). Only used on wxGTK.
  */
  bool OverlayScrollbars() const
    { return m_overlayScrollbars; }

  //! Define if the worksheet should use GTK's overlay scrollbars.
  void OverlayScrollbars(bool overlay){m_overlayScrollbars = overlay;}

  //! Hide brackets that are not under the pointer?
  double PrintScale() const
    { return m_printScale; }

  //! Define if we want to hide brackets that are not under the pointer.
  void PrintScale(double scale){m_printScale = scale;}

  void PrintMargin_Top(double margin){m_printMargin_Top = margin;}
  void PrintMargin_Bot(double margin){m_printMargin_Bot = margin;}
  void PrintMargin_Left(double margin){m_printMargin_Left = margin;}
  void PrintMargin_Right(double margin){m_printMargin_Right = margin;}
  double PrintMargin_Top() const {return m_printMargin_Top;}
  double PrintMargin_Bot() const {return m_printMargin_Bot;}
  double PrintMargin_Left() const {return m_printMargin_Left;}
  double PrintMargin_Right() const {return m_printMargin_Right;}

  //! Sets the zoom factor the worksheet is displayed at
  void SetZoomFactor(double newzoom);

  //! Sets the zoom factor without storing the new value in the config file/registry.
  void SetZoomFactor_temporarily(double newzoom){
    if(m_zoomFactor != newzoom)
      RecalculateForce();
    m_zoomFactor = newzoom;
  }

  /*! Scales a distance [in pixels] according to the zoom factor

    Is used for displaying/printing/exporting of text/maths.
  */
  wxCoord Scale_Px(double px) const;
  AFontSize Scale_Px(AFontSize size) const;

  //! Determines the zoom factor the worksheet is displayed at
  double GetZoomFactor() const
    { return m_zoomFactor; }

  //! Get a drawing context suitable for size calculations
  wxDC *GetRecalcDC() const
    { return m_renderContext.GetRecalcDC(); }

  void SetRecalcDC(wxDC *dc)
    { m_renderContext.SetRecalcDC(dc); }

  wxString GetFontName(TextStyle ts = TS_CODE_DEFAULT) const;

  // cppcheck-suppress functionStatic
  // cppcheck-suppress functionConst
  wxString GetSymbolFontName() const;

  //! Returns a list of chars we need to escape in maxima
  static const wxString CharsNeedingQuotes()
    {
      return wxString(wxS("°,\\'\"()[]-{}^+*/&§?:;=#<>$"));
    }

  wxFontWeight IsBold(long st) const;

  wxFontStyle IsItalic(long st) const;

  bool IsUnderlined(long st) const {return m_styleStore[st].IsUnderlined();}

  /*! Get the width of worksheet labels [in unscaled pixels]

    \todo Should the font size be regarded here?
  */
  wxCoord GetLabelWidth() const
    { return m_labelWidth * 14; }

  //! Get the width of worksheet labels [in chars]
  long LabelWidth() const
    { return m_labelWidth; }
  //! Set the width of worksheet labels [in chars]
  void LabelWidth(long labelWidth)
    {
      if(m_labelWidth != labelWidth)
        RecalculateForce();
      m_labelWidth = labelWidth;
    }

  //! Get the indentation of GroupCells.
  wxCoord GetIndent() const
    {
      if (m_indent < 0)
        return 3 * GetCellBracketWidth() / 2;
      else
        return m_indent;
    }

  /*! Get the resolution

    During recalculation the drawing context doesn't know the display's resolution
    so we need to do some additional tricks here.
  */
  wxSize GetPPI() const;
  int AutosaveMinutes() const {return m_autoSaveMinutes;}
  void AutosaveMinutes(int minutes){m_autoSaveMinutes = minutes;}

  //! How much vertical space is to be left between two group cells?
  long GetCursorWidth() const
    {
      long ppi;

      if(!GetPrinting())
        ppi = GetPPI().x;
      else
        ppi = 96;

      if (ppi / 45 < 1)
        return 1;
      else
        return ppi / 45;
    }

  //! The y position the worksheet starts at
  long GetBaseIndent() const
    {
      if (GetCursorWidth() < 12)
        return 12;
      else
        return 4 + GetCursorWidth();
    }

  //! The vertical space between GroupCells
  long GetGroupSkip() const
    {
      if (GetCursorWidth() < 10)
        return 20;
      else
        return 10 + GetCursorWidth();
    }

  /*! Set the indentation of GroupCells

    Normally this parameter is automatically calculated
  */
  void SetIndent(long indent)
    {
      if(m_indent != indent)
        RecalculateForce();
      m_indent = indent;
    }

  bool IncrementalSearch() const {return m_incrementalSearch;}


  void IncrementalSearch(bool incrementalSearch) { m_incrementalSearch = incrementalSearch; }

  int MaxLayoutTime() const { return m_maxLayoutTime; }
  void MaxLayoutTime(int time) { m_maxLayoutTime = time; }

  enum class LayoutStrategy {
    layout2DWheneverPossible = 0,
    layout2DIfFits = 1,
    layoutPrefer1D = 2
  };
  LayoutStrategy GetLayoutStrategy() const { return m_layoutStrategy; }
  void SetLayoutStrategy(LayoutStrategy s) { m_layoutStrategy = s; }

  void SetLayoutDeadline(int seconds) {
    m_renderContext.SetLayoutDeadline(seconds);
  }
  void ClearLayoutCancelled() {
    m_renderContext.ClearLayoutCancelled();
  }
  bool IsLayoutCancelled() const {
    return m_renderContext.IsLayoutCancelled();
  }

  //! To be called if a font has changed
  void FontChanged()
    {
      RecalculateForce();
    }

  //! Calculates the default line width for the worksheet
  double GetDefaultLineWidth() const
    {
      if (GetZoomFactor() < 1.0)
        return 1.0;
      else
        return GetZoomFactor();
    }

  //! The minimum sensible line width in widths of a letter.
  long LineWidth_em() const
    {
      if(!GetPrinting())
        return m_lineWidth_em;
      else
        return 10000;
    }

  bool AutoSaveAsTempFile() const {return m_autoSaveAsTempFile;}
  void AutoSaveAsTempFile(bool asTempFile){m_autoSaveAsTempFile = asTempFile;}

  //! Set the minimum sensible line width in widths of a letter.
  void LineWidth_em(long width)
    { m_lineWidth_em = width; }

  //! Returns the maximum sensible width for a text line [in characters]:
  // On big 16:9 screens text tends to get \b very wide before it hits the right margin.
  // But text blocks that are 1 meter wide and 2 cm high feel - weird.
  long GetLineWidth() const;

  bool SaveUntitled() const { return m_saveUntitled;}
  void SaveUntitled(bool save) {m_saveUntitled = save;}

  bool CursorJump() const { return m_cursorJump;}
  void CursorJump(bool save){m_cursorJump = save;}

  bool NumpadEnterEvaluates() const { return m_numpadEnterEvaluates;}
  void NumpadEnterEvaluates(bool eval){m_numpadEnterEvaluates = eval;}

  bool SaveImgFileName() const { return m_saveImgFileName;}
  void SaveImgFileName(bool save) { m_saveImgFileName = save;}

  //! Do we want to have automatic line breaks for text cells?
  bool GetAutoWrap() const
    { return m_autoWrap > 0; }

  //! Do we want to have automatic line breaks for code cells?
  bool GetAutoWrapCode() const
    { return m_autoWrap > 1; }

  /*! Sets the auto wrap mode

    Out-of-range values (including the pre-tri-state legacy value 3 that old
    configurations persist) are clamped to "text only", so code wrapping is
    strictly opt-in.

    \param autoWrap
    - 0: No automatic line breaks
    - 1: Automatic line breaks only for text cells
    - 2: Automatic line breaks for text and code cells.
  */
  void SetAutoWrap(long autoWrap){
    m_autoWrap = autoWrap;
    if (m_autoWrap < 0)
      m_autoWrap = 0;
    if (m_autoWrap > 2)
      m_autoWrap = 1;
  }

  //! Do we want automatic indentation?
  bool GetAutoIndent() const
    { return m_autoIndent; }

  void SetAutoIndent(bool autoIndent){m_autoIndent = autoIndent;}

  //! Do we want to indent all maths?
  bool IndentMaths() const {return m_indentMaths;}
  void IndentMaths(bool indent){
    if(m_indentMaths != indent)
      RecalculateForce();
    m_indentMaths = indent;}
  AFontSize GetFontSize(TextStyle st) const { return m_styleStore[st].GetFontSize(); }

  static const wxString &GetStyleName(TextStyle textStyle);

  /*! Reads the style settings

    If a file name is given the settings are read from a file.
  */

  void ReadStyles(const wxString &file = {});

  /*! Saves the style settings

    If a file name is given the settings are written to a file.
  */
  void WriteStyles(const wxString &file = {});
  void WriteStyles(wxConfigBase *config);
  void WriteSettings(const wxString &file = {});
  /*! The single source of truth mapping each persisted text style to its config-key
    prefix.

    ReadStyles() and WriteStyles() both iterate this list, so a style can never be
    read and written under mismatched keys (which used to cause settings to silently
    fail to round-trip). Adding a persisted style is a one-line edit here.
  */
  static const std::vector<std::pair<TextStyle, wxString>> &StyleConfigKeys();

  /*! One mechanically persisted scalar setting: its config key and the member
    variable it is stored in.

    See ScalarConfigSettings().
  */
  struct ScalarSetting {
    //! The key the setting is stored under in the configuration store
    const wxChar *key;
    //! Which member of Configuration holds the setting's cached value
    std::variant<bool Configuration::*, int Configuration::*,
                 long Configuration::*, double Configuration::*,
                 wxString Configuration::*>
        member;
  };
  /*! The single source of truth pairing each mechanical scalar setting's
    config key with the member caching it.

    ReadConfig() and WriteStyles() both iterate this table, so a setting can
    never be read and written under mismatched keys (which used to cause
    settings to silently fail to round-trip). Settings whose read or write
    needs additional logic (value clamping, format migration, the randomized
    configID) are handled individually instead of being listed here.
    Persisting a new plain scalar setting is a one-line edit here.
  */
  static const std::vector<ScalarSetting> &ScalarConfigSettings();
  void MakeStylesConsistent();
  void Outdated(bool outdated)
    { m_outdated = outdated; }

  //! Do we want to display "%e" as "%e" and "%pi" as "%pi"?
  bool CheckKeepPercent() const
    { return m_keepPercent; }

  //! Do we want to display "%e" as "%e" and "%pi" as "%pi"?
  void SetKeepPercent(bool keepPercent)
    {
      if(m_keepPercent != keepPercent)
        RecalculateForce();
      m_keepPercent = keepPercent;
    }

  wxString GetTeXCMRI() const
    { return m_fontCMRI; }

  wxString GetTeXCMSY() const
    { return m_fontCMSY; }

  wxString GetTeXCMEX() const
    { return m_fontCMEX; }

  wxString GetTeXCMMI() const
    { return m_fontCMMI; }

  wxString GetTeXCMTI() const
    { return m_fontCMTI; }

  bool ShowCodeCells() const
    { return m_showCodeCells; }

  void ShowCodeCells(bool show);

  /*! Are we currently printing?

    This affects the bitmap scale as well as the fact if we want
    to output objects that are outside the region that currently is
    redrawn.
  */
  void SetPrinting(bool printing);

  /*! Are we currently printing?

    This affects the bitmap scale as well as the fact if we want
    to output objects that are outside the region that currently is
    redrawn.
  */
  bool GetPrinting() const
    { return m_renderContext.Printing(); }

  //! Gets the color for a text style
  wxColour GetColor(TextStyle style);


  bool UsePngCairo() const { return m_usepngCairo;}
  void UsePngCairo(bool usepngCairo) { m_usepngCairo = usepngCairo;}

  bool GetMatchParens() const { return m_matchParens; }
  void SetMatchParens(bool matchParens) { m_matchParens = matchParens; }
  bool ShowMatchingParens() const { return m_showMatchingParens; }
  void ShowMatchingParens(bool show) { m_showMatchingParens = show; }

  //! Use Unicode centered dots for multiplication signs?
  bool GetChangeAsterisk() const{ return m_changeAsterisk; }

  //! Use Unicode centered dots for multiplication signs?
  void SetChangeAsterisk(bool changeAsterisk) {
    if(m_changeAsterisk != changeAsterisk)
      RecalculateForce();
    m_changeAsterisk = changeAsterisk;
  }

  bool HidemultiplicationSign() const {return m_hidemultiplicationsign;}

  void HidemultiplicationSign(bool show) {
    if(m_hidemultiplicationsign != show)
      RecalculateForce();
    m_hidemultiplicationsign = show;
  }

  bool Latin2Greek() const
    {return m_latin2greek;}

  void Latin2Greek(bool latin2greek) {
    if(m_latin2greek != latin2greek)
      RecalculateForce();
    m_latin2greek = latin2greek;
  }

  bool GreekSidebar_ShowLatinLookalikes() const {return m_greekSidebar_ShowLatinLookalikes;}
  void GreekSidebar_ShowLatinLookalikes(bool show){m_greekSidebar_ShowLatinLookalikes = show;}

  bool GreekSidebar_Show_mu() const {return m_greekSidebar_Show_mu;}
  void GreekSidebar_Show_mu(bool show) {m_greekSidebar_Show_mu = show;}

  wxString SymbolPaneAdditionalChars() const
    {return m_symbolPaneAdditionalChars;}
  void SymbolPaneAdditionalChars(wxString symbols) {m_symbolPaneAdditionalChars = std::move(symbols);}

  //! Notify the user if maxima is idle?
  bool NotifyIfIdle() const
    { return m_notifyIfIdle; }

  void NotifyIfIdle(bool notify) {m_notifyIfIdle = notify;}

  /*! Returns the maximum number of displayed digits

    m_displayedDigits is always >= 20, so we can guarantee the number we return to be unsigned.
  */
  long GetDisplayedDigits() const
    { return m_displayedDigits; }

  void SetDisplayedDigits(long displayedDigits)
    {
      wxASSERT_MSG(displayedDigits >= 0, _("Bug: Maximum number of digits that is to be displayed is too low!"));
      if(m_displayedDigits != displayedDigits)
        RecalculateForce();
      m_displayedDigits = displayedDigits;
    }

  //! Stores the information about a file we need to write during the save process
  class FileToSave
  {
  public:
    FileToSave(const wxString &filename, const wxMemoryBuffer &data):
      m_data(data),
      m_filename(filename)
      {
      }
    const wxString FileName() const{return m_filename;}
    const wxMemoryBuffer Data() const{return m_data;}
  private:
    const wxMemoryBuffer m_data;
    const wxString m_filename;
  };

  //! Stores the information about a file we need to write during the save process
  class TextsnippetToDraw
  {
  public:
    TextsnippetToDraw(const wxPoint &pos, const wxString &text, const wxColor &color):
      m_pos(pos),
      m_text(text),
      m_color(color)
      {
      }
    const wxPoint  Pos() const{return m_pos;}
    const wxString Text() const{return m_text;}
    const wxColor  Color() const{return m_color;}
  private:
    const wxPoint  m_pos;
    const wxString m_text;
    const wxColor  m_color;
  };

  void PushFileToSave(const wxString &filename, const wxMemoryBuffer &data)
    { m_filesToSave.emplace_front(FileToSave(filename, data)); }

  wxRect GetUpdateRegion() const {return m_renderContext.GetUpdateRegion();}
  const std::list<FileToSave> &GetFilesToSave() const {return m_filesToSave;}
  void ClearFilesToSave () { m_filesToSave.clear();}
  void SetUpdateRegion(wxRect rect){m_renderContext.SetUpdateRegion(rect);}

  //! Whether any part of the given rectangle is within the current update region,
  //! or true if drawing is not clipped to update region.
  bool InUpdateRegion(wxRect rect) const;

  bool GetInsertAns() const
    { return m_insertAns; }

  void SetInsertAns(bool insertAns){ m_insertAns = insertAns; }

  bool GetOpenHCaret() const
    { return m_openHCaret; }

  void SetOpenHCaret(bool openHCaret){ m_openHCaret = openHCaret; }

  bool RestartOnReEvaluation() const
    { return m_restartOnReEvaluation; }

  void RestartOnReEvaluation(bool arg){ m_restartOnReEvaluation = arg; }

  //! Reads the size of the current worksheet's visible window. See SetCanvasSize
  wxSize GetCanvasSize() const
    { return m_renderContext.GetCanvasSize(); }

  //! Sets the size of the current worksheet's visible window.
  void SetCanvasSize(wxSize siz)
    {
      if(GetCanvasSize().GetWidth() != siz.GetWidth() ||
         GetCanvasSize().GetHeight() != siz.GetHeight())
        RecalculateForce();
      m_renderContext.SetCanvasSize(siz);
    }

  //! Show the cell brackets [displayed left to each group cell showing its extend]?
  bool ShowBrackets() const
    { return m_showBrackets; }

  bool ShowBrackets(bool show)
    {
      if(m_showBrackets != show)
        RecalculateForce();
      return m_showBrackets = show;
    }

  //! Print the cell brackets [displayed left to each group cell showing its extend]?
  bool PrintBrackets() const
    { return m_printBrackets; }

  showLabels GetLabelChoice() const
    { return m_showLabelChoice; }

  bool ShowInputLabels() const {return m_showInputLabels;}
  void ShowInputLabels(bool show) {
    if(m_showInputLabels != show)
      RecalculateForce();
    m_showInputLabels = show;
  }

  //! How wxMaxima chooses between the light and the dark style set.
  enum class Appearance { light, dark, followSystem };
  Appearance GetAppearance() const { return m_appearance; }
  //! Choose the appearance and update which style set is active.
  void SetAppearance(Appearance a) {
    m_appearance = a;
    m_styleStore.SetUseDark(UseDarkMode());
    UpdateBackgroundBrush();
  }
  //! Refresh the cached document-background brush from the active style set.
  void UpdateBackgroundBrush();
  //! Whether the dark style set is currently active.
  bool UseDarkMode() const {
    if (m_appearance == Appearance::dark) return true;
    if (m_appearance == Appearance::light) return false;
    return SystemIsDark();
  }
  //! Whether the OS reports a dark appearance (false where wx cannot tell).
  static bool SystemIsDark();

  long RecentItems(){return std::max(m_recentItems, static_cast<long>(0));}
  void RecentItems(long items){ m_recentItems = items; }

  //! Do we want to show maxima's automatic labels (%o1, %t1, %i1,...)?
  bool ShowAutomaticLabels() const
    { return (m_showLabelChoice < labels_useronly); }

  //! Do we want at all to show labels?
  bool UseUserLabels() const
    { return m_showLabelChoice > labels_automatic; }

  //! Do we want at all to show labels?
  bool ShowLabels() const
    { return m_showLabelChoice < labels_none; }

  //! Sets the value of the Configuration ChoiceBox that treads displaying labels
  void SetLabelChoice(showLabels choice) {
    if(m_showLabelChoice != choice)
      RecalculateForce();
    m_showLabelChoice = choice;
  }

  bool PrintBrackets(bool print)
    {
      m_printBrackets = print;
      return print;
    }

  //! Autodetect maxima's location? (If false the user-specified location is used)
  bool AutodetectMaxima() const {return m_autodetectMaxima;}
  //! Autodetect maxima's location?
  void AutodetectMaxima(bool autodetectmaxima){m_autodetectMaxima = autodetectmaxima;}

  //! Parameters to the maxima binary
  wxString MaximaParameters() const {return m_maximaParameters;}
  //! The parameters we pass to the maxima binary
  void MaximaParameters(wxString parameters){m_maximaParameters = std::move(parameters);}

  //! The auto-detected maxima location
  static wxString MaximaDefaultLocation();

  //! Returns the location of the maxima binary.
  wxString MaximaLocation() const;

  //! Returns the location of the maxima binary the user has selected.
  wxString MaximaUserLocation() const {return m_maximaUserLocation;}

  //! Sets the location of the maxima binary.
  void MaximaUserLocation(wxString maxima) { m_maximaUserLocation = std::move(maxima); }

  //! Autodetect the web browser? (If false the user-specified location is used)
  bool AutodetectHelpBrowser() const {return m_autodetectHelpBrowser;}
  //! Autodetect the web browser?
  void AutodetectHelpBrowser(bool autodetect){m_autodetectHelpBrowser = autodetect;}

  //! Use the internal help browser? If not a external web browser is used.
  bool InternalHelpBrowser() const {return m_useInternalHelpBrowser && OfferInternalHelpBrowser();}
  //! Use the internal help browser? If not a external web browser is used.
  void InternalHelpBrowser(bool useInternalHelpBrowser)
    {m_useInternalHelpBrowser = useInternalHelpBrowser;}

  //! Prefer the single-page manual?
  bool SinglePageManual() const {return m_singlePageManual;}
  //! Prefer the single-page manual?
  void SinglePageManual(bool singlePageManual)
    {m_singlePageManual = singlePageManual;}

  //! Returns the location of the web browser the user has selected.
  wxString HelpBrowserUserLocation() const {return m_helpBrowserUserLocation;}

  //! Sets the location of the web browser the user has detected.
  void HelpBrowserUserLocation(wxString helpBrowser) { m_helpBrowserUserLocation = std::move(helpBrowser);}

  /*! Could a maxima binary be found in the path we expect it to be in?

    \param location The location to search for maxima in.
    If location == wxEmptyString the default location from the configuration
    is taken.
  */
  static wxString FindProgram(const wxString &location);

  //! Renumber out-of-order cell labels on saving.
  bool FixReorderedIndices() const
    { return m_fixReorderedIndices; }

  void FixReorderedIndices(bool fix) { m_fixReorderedIndices = fix;}

  //! Returns the URL MathJaX can be found at.
  wxString MathJaXURL() const {
    if(m_mathJaxURL_UseUser)
      return m_mathJaxURL;
    else
      return MathJaXURL_Auto();}
  wxString MathJaXURL_User() const { return m_mathJaxURL;}
  bool MathJaXURL_UseUser() const { return m_mathJaxURL_UseUser;}
  void MathJaXURL_UseUser(bool useUser){m_mathJaxURL_UseUser = useUser;}

  bool EnterEvaluates() const {return m_enterEvaluates;}
  void EnterEvaluates(bool enterEvaluates) {m_enterEvaluates = enterEvaluates;}
  static wxString MathJaXURL_Auto() { return wxS("https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js");}
  //! Returns the URL MathJaX can be found at.
  void MathJaXURL(wxString url){m_mathJaxURL = std::move(url);}

  bool CopyBitmap() const {return m_copyBitmap;}
  void CopyBitmap(bool copyBitmap){ m_copyBitmap = copyBitmap; }

  bool CopyMathML() const {return m_copyMathML;}
  void CopyMathML(bool copyMathML){ m_copyMathML = copyMathML;}
  bool CopyMathMLHTML() const {return m_copyMathMLHTML;}
  void CopyMathMLHTML(bool copyMathMLHTML){ m_copyMathMLHTML = copyMathMLHTML; }
  bool HideMarkerForThisMessage(wxString message);
  void HideMarkerForThisMessage(const wxString &message, bool hide)
    {m_hideMarkerForThisMessage[message] = hide;}
  bool CopyRTF() const {return m_copyRTF;}
  void CopyRTF(bool copyRTF) { m_copyRTF = copyRTF; }
  bool CopySVG() const {return m_copySVG;}
  void CopySVG(bool copySVG) { m_copySVG = copySVG; }
  bool CopyEMF() const {return m_copyEMF;}
  void CopyEMF(bool copyEMF) { m_copyEMF = copyEMF; }
  bool UseSVG() const {return m_useSVG;}
  void UseSVG(bool useSVG) { m_useSVG = useSVG ;}
  //! The choice for the "maximum output length to display" setting
  void ShowLength(long length) {
    m_showLength = length;
  }
  //! The choice for the "maximum output length to display" setting
  long ShowLength() const {return m_showLength;}
  //! The "maximum output length to display" setting in cells
  std::size_t ShowLength_Bytes() const;

  //! Which way do we want to draw parenthesis?
  void SetParenthesisDrawMode(drawMode mode) {
    if(m_parenthesisDrawMode != mode)
      RecalculateForce();
    m_parenthesisDrawMode = mode;
  }

  void TocShowsSectionNumbers(bool showSectionNumbers) { m_TOCshowsSectionNumbers = showSectionNumbers; }

  bool TocShowsSectionNumbers() const {return m_TOCshowsSectionNumbers;}

  void UseUnicodeMaths(bool useunicodemaths)
    { m_useUnicodeMaths = useunicodemaths; }
  bool UseUnicodeMaths() const {return m_useUnicodeMaths;}

  /*! Get the text Style for a given text style identifier.

    \param textStyle The text style to resolve the style for.
  */
  const Style *GetStyle(TextStyle textStyle) const { return &m_styleStore[textStyle]; }
  /*! Get the text Style for a given text style identifier.

    Theoretically GetStyle and GetWritableStyle wouldn't collide if they had the
    same name. But I am afraid if all developers know how to deal with the situation
    that performance degrades if a const is missing while the rest works fine.
    \param textStyle The text style to resolve the style for.
  */
  Style *GetWritableStyle(TextStyle textStyle) { return &m_styleStore[textStyle]; }

  /*! Get the worksheet canvas this configuration storage is valid for

    Deliberately typed as the generic scrolled-canvas base class: the cells
    only need window services (DPI, scroll offsets, accessibility parent),
    never the Worksheet class itself.
  */
  wxScrolledCanvas *GetWorkSheet() const {return m_workSheet;}
  //! Set the worksheet canvas this configuration storage is valid for
  void SetWorkSheet(wxScrolledCanvas *workSheet);

  /*! The registry of special cells (selection, active cell, errors, ...) of this document

    Cells constructed with this configuration register themselves here. Set by the
    Worksheet that owns the CellPointers object; null for configurations that never
    construct cells (e.g. the temporary print configuration).
  */
  //! The document-model half of the cell-pointer registry (null if none).
  DocumentCellPointers *GetDocumentCellPointers() const {return m_documentCellPointers;}
  //! Register the document-model half of the cell-pointer registry.
  void SetDocumentCellPointers(DocumentCellPointers *p) {m_documentCellPointers = p;}
  //! The transient view-state half of the cell-pointer registry (null if none).
  ViewCellPointers *GetViewCellPointers() const {return m_viewCellPointers;}
  //! Register the transient view-state half of the cell-pointer registry.
  void SetViewCellPointers(ViewCellPointers *p) {m_viewCellPointers = p;}

  /*! Ask the view (if any) to recalculate the layout starting at the given group cell

    How GroupCell::MarkNeedsRecalculate() reaches the view without the cells
    depending on the Worksheet class. A no-op for configurations without a
    view (printing, tests).
  */
  void RequestRecalculate(GroupCell *group) const
    {if (m_recalculateRequest) m_recalculateRequest(group);}
  //! Set the callback RequestRecalculate() notifies the view through
  void SetRecalculateRequestCallback(std::function<void(GroupCell *)> callback)
    {m_recalculateRequest = std::move(callback);}

  /*! Ask the view to schedule a recalculation of the WHOLE worksheet.

    RecalculateForce() bumps a global counter that flips every cell's
    ConfigChanged() dirty at once (see CellCfgCnt()); there is no per-cell
    notification, so the view's layout engine would not otherwise learn that
    the cells outside a pending bounded range became dirty. This tells it to
    lay the whole document out. A no-op for configurations without a view
    (printing, tests without a worksheet). */
  void RequestRecalculateAll() const
    {if (m_recalculateAllRequest) m_recalculateAllRequest();}
  //! Set the callback RequestRecalculateAll() / RecalculateForce() notify through.
  void SetRecalculateAllRequestCallback(std::function<void()> callback)
    {m_recalculateAllRequest = std::move(callback);}

  long DefaultPort() const {return m_defaultPort;}
  void DefaultPort(long port){m_defaultPort = port;}
  bool GetAbortOnError() const {return m_abortOnError;}
  void SetAbortOnError(bool abortOnError) {m_abortOnError = abortOnError;}

  long GetLanguage() const {return m_language;}
  void SetLanguage(long language) {m_language = language;}

  //! The maximum number of Megabytes of gnuplot sources we should store
  long MaxGnuplotMegabytes() const {return m_maxGnuplotMegabytes;}
  void MaxGnuplotMegabytes(long megaBytes)
    {m_maxGnuplotMegabytes = megaBytes;}

  bool OfferKnownAnswers() const {return m_offerKnownAnswers;}
  void OfferKnownAnswers(bool offerKnownAnswers)
    {m_offerKnownAnswers = offerKnownAnswers;}

  /*! Announce maxima's output to screen readers as MathML?

    With false (the default) the accessible name of a math cell is its linear
    maths text (ToString()); with true it is the cell's MathML representation,
    for screen readers that can speak MathML properly.
  */
  bool ScreenReaderAnnouncesMathML() const {return m_screenReaderAnnouncesMathML;}
  void ScreenReaderAnnouncesMathML(bool mathML)
    {m_screenReaderAnnouncesMathML = mathML;}

  wxString Documentclass() const {return m_documentclass;}
  void Documentclass(wxString clss){m_documentclass = std::move(clss);}
  wxString DocumentclassOptions() const {return m_documentclassOptions;}
  void DocumentclassOptions(wxString classOptions){m_documentclassOptions = std::move(classOptions);}


  htmlExportFormat HTMLequationFormat() const {return m_htmlEquationFormat;}
  void HTMLequationFormat(htmlExportFormat HTMLequationFormat)
    {m_htmlEquationFormat = HTMLequationFormat;}

  AFontSize GetDefaultFontSize() const        { return m_styleStore[TS_CODE_DEFAULT].GetFontSize(); }
  AFontSize GetMathFontSize() const           { return m_styleStore[TS_MATH].GetFontSize(); }

  //! Get the worksheet this configuration storage is valid for
  long GetAutosubscript_Num() const {return m_autoSubscript;}
  void SetAutosubscript_Num(long autosubscriptnum) {m_autoSubscript = autosubscriptnum;}
  wxString GetAutosubscript_string() const;
  //! Determine the default background color of the worksheet
  wxColor DefaultBackgroundColor();
  //! Determine the default background color of editorcells
  wxColor EditorBackgroundColor();
  //! Do we want to save time by only redrawing the area currently shown on the screen?
  bool ClipToDrawRegion() const {return m_renderContext.ClipToDrawRegion();}
  //! Do we want to save time by only redrawing the area currently shown on the screen?
  void ClipToDrawRegion(bool clipToDrawRegion){m_renderContext.ClipToDrawRegion(clipToDrawRegion);}
  void SetVisibleRegion(wxRect visibleRegion){
    if(m_renderContext.GetVisibleRegion().GetWidth() != visibleRegion.GetWidth())
      RecalculateForce();
    m_renderContext.SetVisibleRegion(visibleRegion);
  }
  wxRect GetVisibleRegion() const {return m_renderContext.GetVisibleRegion();}
  void SetWorksheetPosition(wxPoint worksheetPosition){m_renderContext.SetWorksheetPosition(worksheetPosition);}
  wxPoint GetWorksheetPosition() const {return m_renderContext.GetWorksheetPosition();}
  wxString MaximaShareDir() const {return m_maximaSession.ShareDir();}
  void MaximaShareDir(wxString dir){m_maximaSession.ShareDir(std::move(dir));}
  wxString MaximaDemoDir() const {return m_maximaSession.DemoDir();}
  void MaximaDemoDir(wxString dir){m_maximaSession.DemoDir(std::move(dir));}
  void InLispMode(bool lisp){m_maximaSession.InLispMode(lisp);}
  bool InLispMode() const {return m_maximaSession.InLispMode();}
  void BitmapScale(int factor){m_bitmapScale = factor;}
  int BitmapScale() const {return m_bitmapScale;}
  void DefaultPlotHeight(int px){m_defaultPlotHeight = px;}
  int DefaultPlotHeight() const {return m_defaultPlotHeight;}
  void DefaultPlotWidth(int px){m_defaultPlotWidth = px;}
  int DefaultPlotWidth() const {return m_defaultPlotWidth;}
  void DefaultFramerate(double fps){m_defaultFramerate = fps;}
  double DefaultFramerate() const {return m_defaultFramerate;}
  void TocDepth(int depth){m_tocDepth = depth;}
  int TocDepth() const {return m_tocDepth;}
  bool TeXExponentsAfterSubscript() const {return m_TeXExponentsAfterSubscript;}
  void TeXExponentsAfterSubscript(bool ExponentsAfterSubscript)
    {m_TeXExponentsAfterSubscript = ExponentsAfterSubscript;}
  bool UsePartialForDiff() const {return m_usePartialForDiff;}
  void UsePartialForDiff(bool usePartialForDiff)
    {m_usePartialForDiff = usePartialForDiff;}
  /*! Ask the view (if any) to re-adjust its worksheet (scroll) size

    A cell that changed height calls this so the view recomputes its virtual
    size, without the cell layer depending on the Worksheet class. Mirrors
    RequestRecalculate(); a no-op for configurations without a view (printing,
    tests). The "needs adjustment" flag itself lives on the Worksheet. */
  void RequestAdjustWorksheetSize() const
    {if (m_adjustWorksheetSizeRequest) m_adjustWorksheetSizeRequest();}
  //! Set the callback RequestAdjustWorksheetSize() notifies the view through
  void SetAdjustWorksheetSizeRequestCallback(std::function<void()> callback)
    {m_adjustWorksheetSizeRequest = std::move(callback);}
  //! Record that this cell has been drawn to ReportMultipleRedraws()
  void NotifyOfCellRedraw(const Cell *cell);
  //! Clear the memory of ReportMultipleRedraws()
  void ClearAndEnableRedrawTracing();
  /*! Report if a cell has been redrawn 2 or more times during a simple Draw() command

    Only used in debug mode; If a cell is drawn and then re-drawn in the same draw
    command this most likely means that a 2D-displayed cell draws its sub-cells,
    but didn't remove them from the Cell::NextToDraw()-list of cells to draw
    after this cell has been drawn.
  */
  void ReportMultipleRedraws();
  //! If we decide that the HTML browser in the sidebar doesn't work for every platform...
  bool OfferInternalHelpBrowser() const;
  bool WrapLatexMath() const {return m_wrapLatexMath;}
  void WrapLatexMath(bool wrapLatexMath){m_wrapLatexMath = wrapLatexMath;}
  bool AllowNetworkHelp() const {return m_allowNetworkHelp;}
  void AllowNetworkHelp(bool allowNetworkHelp){m_allowNetworkHelp = allowNetworkHelp;}
  bool ShowAllDigits() const {return m_showAllDigits;}
  void ShowAllDigits(bool shw){
    if(m_showAllDigits != shw)
      RecalculateForce();
    m_showAllDigits = shw;
  }
  bool LineBreaksInLongNums() const {return m_lineBreaksInLongNums;}
  void LineBreaksInLongNums(bool brk){
    if(m_lineBreaksInLongNums != brk)
      RecalculateForce();
    m_lineBreaksInLongNums = brk;
  }
  int  MaxClipbrdBitmapMegabytes() const {return m_maxClipbrd_BitmapMegabytes;}
  void MaxClipbrdBitmapMegabytes(int maxClipbrd_BitmapMegabytes)
    {m_maxClipbrd_BitmapMegabytes = maxClipbrd_BitmapMegabytes;}

  void MaximaUsesHtmlBrowser(bool maximaUsesHhtmlBrowser){m_maximaUsesHhtmlBrowser = maximaUsesHhtmlBrowser;}
  bool MaximaUsesHtmlBrowser() const {return m_maximaUsesHhtmlBrowser;}
  void MaximaUsesWxmaximaBrowser(bool maximaUsesWxmaximaBrowser){m_maximaUsesWxmaximaBrowser = maximaUsesWxmaximaBrowser;}
  bool MaximaUsesWxmaximaBrowser() const {return m_maximaUsesWxmaximaBrowser && OfferInternalHelpBrowser();}
  void ExportContainsWXMX(bool exportContainsWXMX){m_exportContainsWXMX = exportContainsWXMX;}
  bool ExportContainsWXMX() const {return m_exportContainsWXMX;}
  void WizardTab(long tab){m_wizardTab = tab;}
  long WizardTab() const {return m_wizardTab;}

  void Display2d_Unicode(bool unicode){m_display2d_Unicode = unicode;}
  bool Display2d_Unicode() const {return m_display2d_Unicode;}

  #ifdef __WXMSW__
  bool UseWGnuplot() const {return m_useWgnuplot;}
  void UseWGnuplot(bool usewgnuplot) {m_useWgnuplot = usewgnuplot;}
  #endif
  wxString TexPreamble() const {return m_texPreamble;}
  void TexPreamble(wxString texPreamble) {m_texPreamble = std::move(texPreamble);}

  void SetMaximaVersion(const wxString &version){m_maximaSession.SetMaximaVersion(version);}
  wxString GetMaximaVersion() const {return m_maximaSession.GetMaximaVersion();}
  void SetMaximaArch(const wxString &arch){m_maximaSession.SetMaximaArch(arch);}
  wxString GetMaximaArch() const {return m_maximaSession.GetMaximaArch();}
  void SetLispVersion(const wxString &version){m_maximaSession.SetLispVersion(version);}
  wxString GetLispVersion() const {return m_maximaSession.GetLispVersion();}
  void SetLispType(const wxString &type){m_maximaSession.SetLispType(type);}
  wxString GetLispType() const {return m_maximaSession.GetLispType();}

  //! Initialize the text styles on construction.
  void InitStyles();
  //! True if we are confident that the font renders this char
  bool FontRendersChar(wxUniChar ch, const wxFont &font = *wxNORMAL_FONT)
    { return m_fontRenderability.FontRendersChar(ch, font); }

  //! Which styles affect how code is displayed?
  const std::vector<TextStyle> &GetCodeStylesList() const {return m_styleStore.CodeStylesList();}
  //! Which styles affect how math output is displayed?
  const std::vector<TextStyle> &GetMathStylesList() const {return m_styleStore.MathStylesList();}
  //! Which styles affect only colors?
  const std::vector<TextStyle> &GetColorOnlyStylesList() const {return m_styleStore.ColorOnlyStylesList();}
  bool StyleAffectsCode(TextStyle style) const;
  bool StyleAffectsMathOut(TextStyle style) const;
  bool StyleAffectsColorOnly(TextStyle style) const;
  //! true means: The system's config storage has changed since the configuration has been read
  bool UpdateNeeded() const;
  //! Enable costly checks
  static void SetDebugmode(){m_debugMode = true;}
  //! Enable costly checks?
  static bool GetDebugmode(){return m_debugMode;}
  maximaHelpFormat MaximaHelpFormat() const;
  void MaximaHelpFormat(maximaHelpFormat format) {m_maximaHelpFormat = format;}

private:
  //! What we know about the connected Maxima instance
  MaximaSessionInfo m_maximaSession;
  //! The options this class got at initialization time
  InitOpt m_initOpts;
  /*! The id of the current configuration

    If the ID in the operating system's config storage differs from this one someone
    has changed the configuration.
  */
  long m_configId;
  //! Our random device
  std::random_device m_rd;
  //! Our random engine
  std::default_random_engine m_eng;
  wxEnvVariableHashMap m_maximaEnvVars;
public:
  //! One sample of OS entropy (std::random_device).
  std::random_device::result_type RandomEntropy(){return m_rd();}
  //! The pseudo-random engine, e.g. to feed a distribution.
  std::default_random_engine &RandomEngine(){return m_eng;}
  /*! A counter that increases every time we need to recalculate all worksheet cells

    If that counter eventually wraps that should be fine: The cells will have been
    recalculated before that and if someone trying to do a speed run with wxMaxima
    manages to accumulate billions of config changes before the worksheet manages
    to recalculate itself the worst thing that can happen is a visual glitch.
  */
  std::int_fast32_t CellCfgCnt() const {return m_cellCfgCnt;}
  /*! Force every cell to be recalculated on the next layout pass.

    Bumps the global config counter (so every cell's ConfigChanged() reports
    dirty) AND notifies the view's layout engine to schedule a whole-document
    pass - the counter bump alone is invisible to the engine's per-cell dirty
    tracking, which would leave cells outside a pending bounded range stale. */
  void RecalculateForce() {
    m_cellCfgCnt++;
    RequestRecalculateAll();
  }
  static bool UseThreads(){return m_use_threads;}
  static void UseThreads(bool use){m_use_threads = use;}
  static void SetMaximaLang(const wxString &LANG){m_maxima_LANG = LANG;}
  static wxString GetMaximaLang(){return m_maxima_LANG;}
private:
  //! Which LANG environment variable to communicate to maxima?
  static wxString m_maxima_LANG;
  std::list<FileToSave> m_filesToSave;
  //! The text styles (light + dark palettes)
  Styles m_styleStore;
  //! Messages the user has told us not to show a marker for anymore
  StringBoolHash m_hideMarkerForThisMessage;
  //! Which characters can each font render? (persisted probe cache)
  FontRenderabilityCache m_fontRenderability;
  mathDisplayMode m_displayMode = display_2d;
  static bool m_debugMode;
  bool m_showInputLabels;
  long m_wizardTab;
  bool m_display2d_Unicode;
  bool m_usePartialForDiff;
  bool m_maximaUsesHhtmlBrowser;
  bool m_maximaUsesWxmaximaBrowser;
  //! true = Autosave doesn't save into the current file.
  bool m_autoSaveAsTempFile;
  //! The number of the language wxMaxima uses.
  long m_language;
  //! Autodetect maxima's location?
  bool m_autodetectMaxima;
  //! Autodetect the help browser?
  bool m_autodetectHelpBrowser;
  //! Use the internal help browser?
  bool m_useInternalHelpBrowser;
  //! Prefer the single-page manual?
  bool m_singlePageManual;
  //! Do we want to use incremental search?
  bool m_incrementalSearch;
  //! Which objects do we want to convert into subscripts if they occur after an underscore?
  long m_autoSubscript;
  //! The worksheet canvas this configuration storage is valid for
  wxScrolledCanvas *m_workSheet = NULL;
  //! The document-model half of the cell registry. Not copied.
  DocumentCellPointers *m_documentCellPointers = NULL;
  //! The transient view-state half of the cell registry. Not copied.
  ViewCellPointers *m_viewCellPointers = NULL;
  //! The view's recalculation-request callback (see RequestRecalculate()). Not copied.
  std::function<void(GroupCell *)> m_recalculateRequest;
  //! The view's whole-document recalculation callback (see RequestRecalculateAll()). Not copied.
  std::function<void()> m_recalculateAllRequest;
  //! The view's adjust-size-request callback (see RequestAdjustWorksheetSize()). Not copied.
  std::function<void()> m_adjustWorksheetSizeRequest;
  //! The state of the current render pass (DCs, canvas, clipping, deadline)
  RenderContext m_renderContext;
  bool m_wrapLatexMath;
  bool m_allowNetworkHelp;
  bool m_exportContainsWXMX;
  wxString m_texPreamble;

  drawMode m_parenthesisDrawMode;
  bool m_TeXExponentsAfterSubscript;
  wxString m_helpBrowserUserLocation;
  wxString m_maximaUserLocation;
  //! Hide brackets that are not under the pointer
  bool m_hideBrackets;
  //! Use GTK's overlay scrollbars on the worksheet? See OverlayScrollbars().
  bool m_overlayScrollbars;
  //! The scale for printing
  double m_printScale;
  double m_printMargin_Top;
  double m_printMargin_Bot;
  double m_printMargin_Left;
  double m_printMargin_Right;
  //! Show the cell brackets [displayed left to each group cell showing its extend]?
  bool m_showBrackets;
  //! Prlong the cell brackets [displayed left to each group cell showing its extend]?
  bool m_printBrackets;
  /*! Replace a "*" by a centered dot?

    Normally we ask the parser for this piece of information. But during recalculation
    of widths while selecting text we don't know our parser.
  */
  bool m_changeAsterisk;
  //! Notify the user if maxima is idle
  bool m_notifyIfIdle;
  //! How many digits of a number we show by default?
  long m_displayedDigits;
  //! Automatically wrap long lines?
  long m_autoWrap;
  //! Automatically indent long lines?
  bool m_autoIndent;
  //! Always show all digits of all numbers?
  bool m_showAllDigits;
  //! Allow linebreaks in numbers that are longer than a line?
  bool m_lineBreaksInLongNums;
  //! Do we want to automatically close parenthesis?
  bool m_matchParens;
  //! Do we want to automatically insert new cells containing a "%" at the end of every command?
  bool m_insertAns;
  //! Do we want to automatically open a new cell if maxima has finished evaluating its input?
  bool m_openHCaret;
  //! The width of input and output labels [in chars]
  long m_labelWidth;
  long m_indent;
  bool m_latin2greek;
  double m_zoomFactor;
  bool m_outdated;
  wxString m_maximaParameters;
  bool m_keepPercent;
  bool m_restartOnReEvaluation;
  wxString m_fontCMRI, m_fontCMSY, m_fontCMEX, m_fontCMMI, m_fontCMTI;
  long m_lineWidth_em;
  showLabels m_showLabelChoice;
  bool m_fixReorderedIndices;
  wxString m_mathJaxURL;
  bool m_mathJaxURL_UseUser;
  bool m_showCodeCells;
  bool m_copyBitmap;
  bool m_copyMathML;
  bool m_copyMathMLHTML;
  long m_showLength;
  bool m_usepngCairo;
  bool m_enterEvaluates;
  bool m_useSVG;
  bool m_fixedFontTC;
  bool m_copyRTF;
  bool m_copySVG;
  bool m_copyEMF;
  bool m_TOCshowsSectionNumbers;
  bool m_useUnicodeMaths;
  bool m_indentMaths;
  bool m_abortOnError;
  bool m_showMatchingParens;
  bool m_hidemultiplicationsign;
  bool m_offerKnownAnswers;
  bool m_screenReaderAnnouncesMathML;
  long m_defaultPort;
  long m_maxGnuplotMegabytes;
  long m_defaultPlotHeight;
  long m_defaultPlotWidth;
  bool m_saveUntitled;
  bool m_cursorJump;
  bool m_numpadEnterEvaluates;
  bool m_saveImgFileName;
  wxString m_documentclass;
  wxString m_documentclassOptions;
  htmlExportFormat m_htmlEquationFormat;

  wxColour m_defaultBackgroundColor;
  wxBrush m_tooltipBrush;
  bool m_greekSidebar_ShowLatinLookalikes;
  #ifdef __WXMSW__
  bool m_useWgnuplot = false;
  #endif
  bool m_greekSidebar_Show_mu;
  wxString m_symbolPaneAdditionalChars;
  Appearance m_appearance = Appearance::followSystem;
  long m_recentItems;
  int m_bitmapScale;
  double m_defaultFramerate;
  int m_tocDepth;
  int m_maxClipbrd_BitmapMegabytes;
  int m_autoSaveMinutes;
  int m_maxLayoutTime;
  LayoutStrategy m_layoutStrategy = LayoutStrategy::layout2DIfFits;
  wxString m_wxMathML_Filename;
  maximaHelpFormat m_maximaHelpFormat;
  std::atomic<std::int_fast32_t> m_cellCfgCnt{0};
  static bool m_use_threads;
};

//! Sets the configuration's "printing" flag until this class is left.
class Printing
{
public:
  explicit Printing(Configuration *configuration)
    {
      m_configuration = configuration;
      m_configuration->SetPrinting(true);
    }
  virtual ~Printing()
    {
      m_configuration->SetPrinting(false);
    }
private:
  Configuration * m_configuration;
};

//! Clears the configuration's "Clip to draw region" flag until this class is left.
class NoClipToDrawRegion
{
public:
  explicit NoClipToDrawRegion(Configuration *configuration)
    {
      m_configuration = configuration;
      m_configuration->ClipToDrawRegion(false);
    }
  virtual ~NoClipToDrawRegion()
    {
      m_configuration->ClipToDrawRegion(true);
    }
private:
  Configuration * m_configuration;
};

#endif // CONFIGURATION_H
