<commit_before>/*******************************************************************************

Licensed to the OpenCOR team under one or more contributor license agreements.
See the NOTICE.txt file distributed with this work for additional information
regarding copyright ownership. The OpenCOR team licenses this file to you under
the Apache License, Version 2.0 (the "License"); you may not use this file
except in compliance with the License. You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied. See the License for the
specific language governing permissions and limitations under the License.

*******************************************************************************/

//==============================================================================
// QScintillaWidget class
//==============================================================================

#include "filemanager.h"
#include "guiutils.h"
#include "qscintillawidget.h"

//==============================================================================

#include <Qt>

//==============================================================================

#include <QDragEnterEvent>
#include <QLabel>
#include <QMenu>
#include <QMimeData>

//==============================================================================

#include "Qsci/qscilexer.h"

//==============================================================================

namespace OpenCOR {
namespace QScintillaSupport {

//==============================================================================

QScintillaWidget::QScintillaWidget(const QString &pContents,
                                   const bool &pReadOnly,
                                   QsciLexer *pLexer, QWidget *pParent) :
    QsciScintilla(pParent),
    mCanUndo(false),
    mCanRedo(false),
    mCanSelectAll(false),
    mOverwriteMode(false)
{
    // Remove the frame around our Scintilla editor

    setFrameShape(QFrame::NoFrame);

    // Remove the margin number in our Scintilla editor

    setMarginWidth(SC_MARGIN_NUMBER, 0);

    // Associate a lexer to our Scintilla editor, should one be provided
    // Note: the default font family and size come from Qt Creator...

#if defined(Q_OS_WIN)
    mFont = QFont("Courier", 10);
#elif defined(Q_OS_LINUX)
    mFont = QFont("Monospace", 9);
#elif defined(Q_OS_MAC)
    mFont = QFont("Monaco", 12);
#else
    #error Unsupported platform
#endif

    if (pLexer) {
        // A lexer was provided, so specify its fonts and associate it with our
        // Scintilla editor

        pLexer->setFont(mFont);

        setLexer(pLexer);

        // Specify the type of tree folding to be used. Some lexers may indeed
        // use that feature, so...

        setFolding(QsciScintilla::BoxedTreeFoldStyle);
    } else {
        // No lexer was provided, so simply specify a default font family and
        // size for our Scintilla editor

        setFont(mFont);
    }

    // Set the contents of our Scintilla editor and its read-only property

    setContents(pContents);
    setReadOnly(pReadOnly);

    // Show the caret line

    setCaretLineVisible(true);

    // Force the use of UNIX EOL mode
    // Note: by default QScintilla will use EolWindows on Windows and EolUnix on
    //       Linux and OS X. However, the fact that it uses EolWindows on
    //       Windows can cause problems on that platform with files not using a
    //       a Windows EOL mode, so...

    setEolMode(EolUnix);

    // Initialise our colours by 'updating' them

    updateColors();

    // Clear some key mappings inherited from QsciScintilla
    // Note #1: indeed, QsciScintilla handles some shortcuts (e.g. Ctrl+L),
    //          which we don't want to see handled (e.g. Ctrl+L is used by
    //          QsciScintilla to delete the current line while OpenCOR uses it
    //          to (un)lock the current file), so...
    // Note #2: even though we are clearing those key mappings, we must also
    //          bypass QsciScintilla's handling of event() (see below). Indeed,
    //          not to do so would mean that if, for example, the user was to
    //          press Ctrl+L, then nothing would happen while we would have
    //          expected the current file to be (un)locked...

    SendScintilla(SCI_CLEARCMDKEY, (SCMOD_CTRL << 16)+'D');
    SendScintilla(SCI_CLEARCMDKEY, (SCMOD_CTRL << 16)+'L');
    SendScintilla(SCI_CLEARCMDKEY, (SCMOD_CTRL << 16)+(SCMOD_SHIFT << 16)+'L');

    // Empty context menu by default

    mContextMenu = new QMenu(this);

    // Create our two labels to show our cursor position and editing mode

    mCursorPositionWidget = new QLabel(this);
    mEditingModeWidget = new QLabel(this);

    // Keep track of the change to the UI

    connect(this, SIGNAL(SCN_UPDATEUI(int)),
            this, SLOT(updateUi()));

    // Keep track of whether we can undo/redo

    connect(this, SIGNAL(textChanged()),
            this, SLOT(updateCanUndoAndCanRedo()));

    // Keep track of changes to our editor that may affect our ability to select
    // all of its text
    // Note: we use the SCN_MODIFIED() signal rather than the textChanged()
    //       signal since the latter is only emitted when inserting or deleting
    //       some text...

    connect(this, SIGNAL(selectionChanged()),
            this, SLOT(checkCanSelectAll()));
    connect(this, SIGNAL(SCN_MODIFIED(int, int, const char *, int, int, int, int, int, int, int)),
            this, SLOT(checkCanSelectAll()));

    // Keep track of the change in the cursor position

    connect(this, SIGNAL(cursorPositionChanged(int, int)),
            this, SLOT(cursorPositionChanged(const int &, const int &)));
}

//==============================================================================

QMenu * QScintillaWidget::contextMenu() const
{
    // Return our context menu

    return mContextMenu;
}

//==============================================================================

void QScintillaWidget::setContextMenu(const QList<QAction *> &pContextMenuActions)
{
    // Set our context menu

    mContextMenu->clear();

    foreach (QAction *action, pContextMenuActions)
        mContextMenu->addAction(action);
}

//==============================================================================

int QScintillaWidget::currentPosition() const
{
    // Return our current position

    return SendScintilla(SCI_GETCURRENTPOS);
}

//==============================================================================

QString QScintillaWidget::contents() const
{
    // Return our contents

    return text();
}

//==============================================================================

void QScintillaWidget::setContents(const QString &pContents)
{
    // Set our contents

    setText(pContents);
}

//==============================================================================

int QScintillaWidget::contentsSize() const
{
    // Return the size of our contents

    return SendScintilla(SCI_GETLENGTH);
}

//==============================================================================

QString QScintillaWidget::textInRange(const int &pStartRange,
                                      const int &pEndRange) const
{
    // Retrieve and return the text in the given range, making sure that the
    // given range makes sense

    int maxRange = contentsSize();

    if (   (pStartRange < 0) || (pStartRange >= maxRange)
        || (pEndRange < 0) || (pEndRange >= maxRange)
        || (pStartRange >= pEndRange))
        return QString();

    char *text = new char[pEndRange-pStartRange+1];

    SendScintilla(SCI_GETTEXTRANGE, pStartRange, pEndRange, text);

    QString res = QString(text);

    delete[] text;

    return res;
}

//==============================================================================

int QScintillaWidget::findTextInRange(const int &pStartRange,
                                      const int &pEndRange,
                                      const QString &pText,
                                      const bool &pCaseSensitive) const
{
    // Find and return the position, if any, of the given text within the given
    // range

    SendScintilla(SCI_SETSEARCHFLAGS, pCaseSensitive?SCFIND_MATCHCASE:0);

    SendScintilla(SCI_SETTARGETSTART, pStartRange);
    SendScintilla(SCI_SETTARGETEND, pEndRange);

    QByteArray text = pText.toUtf8();

    return SendScintilla(SCI_SEARCHINTARGET, text.length(), text.constData());
}

//==============================================================================

bool QScintillaWidget::isSelectAllAvailable() const
{
    // Return whether we can select all the text

    return mCanSelectAll;
}

//==============================================================================

void QScintillaWidget::del()
{
    // Delete the selected text, if any

    SendScintilla(QsciScintillaBase::SCI_CLEAR);
}

//==============================================================================

void QScintillaWidget::resetUndoHistory()
{
    // Reset our undo history

    SendScintilla(SCI_EMPTYUNDOBUFFER);

    mCanUndo = false;
    mCanRedo = false;
}

//==============================================================================

QLabel * QScintillaWidget::cursorPositionWidget() const
{
    // Return our cursort position widget

    return mCursorPositionWidget;
}

//==============================================================================

QLabel * QScintillaWidget::editingModeWidget() const
{
    // Return our editing mode widget

    return mEditingModeWidget;
}

//==============================================================================

QString QScintillaWidget::eolString() const
{
    // Return the end of line we use

    switch (eolMode()) {
    case EolUnix:
        return "\n";
    case EolMac:
        return "\r";
    default:   // EolWindows
        return "\r\n";
    }
}

//==============================================================================

void QScintillaWidget::setBackgroundColor(const int &pStyle,
                                          const QColor &pBackgroundColor)
{
    // Set the background color for the given style

    SendScintilla(QsciScintillaBase::SCI_STYLESETBACK, pStyle, pBackgroundColor);
}

//==============================================================================

void QScintillaWidget::setForegroundColor(const int &pStyle,
                                          const QColor &pForegroundColor)
{
    // Set the foreground color for the given style

    SendScintilla(QsciScintillaBase::SCI_STYLESETFORE, pStyle, pForegroundColor);
}

//==============================================================================

int QScintillaWidget::zoomLevel() const
{
    // Return our zoom level

    return SendScintilla(QsciScintillaBase::SCI_GETZOOM);
}

//==============================================================================

void QScintillaWidget::changeEvent(QEvent *pEvent)
{
    // Default handling of the event

    QsciScintilla::changeEvent(pEvent);

    // Check whether the palette has changed and if so then update our colors

    if (pEvent->type() == QEvent::PaletteChange)
        updateColors();
}

//==============================================================================

void QScintillaWidget::contextMenuEvent(QContextMenuEvent *pEvent)
{
    // Show our context menu or QsciScintilla's one, if we don't have one

    if (mContextMenu->isEmpty())
        QsciScintilla::contextMenuEvent(pEvent);
    else
        mContextMenu->exec(pEvent->globalPos());
}

//==============================================================================

void QScintillaWidget::dragEnterEvent(QDragEnterEvent *pEvent)
{
    // Accept the proposed action for the event, but only if we are not dropping
    // URIs
    // Note: this is not (currently?) needed on Windows and OS X, but if we
    //       don't have that check on Linux, then to drop some files on our
    //       Scintilla editor will result in the text/plain version of the data
    //       (e.g. file:///home/me/myFile) to be inserted in the text, so...

    if (!pEvent->mimeData()->hasFormat(Core::FileSystemMimeType))
        pEvent->acceptProposedAction();
    else
        pEvent->ignore();
}

//==============================================================================

bool QScintillaWidget::event(QEvent *pEvent)
{
    // Bypass QsciScintilla's handling of event()
    // Note: see the note on the clearing of some key mappings in the contructor
    //       above...

    return QsciScintillaBase::event(pEvent);
}

//==============================================================================

void QScintillaWidget::keyPressEvent(QKeyEvent *pEvent)
{
    // Let people know that a key has been pressed

    bool handled = false;

    emit keyPressed(pEvent, handled);

    // Carry on as normal, if the event wasn't handled

    if (!handled) {
        // Reset the font size, if needed

        if (   !(pEvent->modifiers() & Qt::ShiftModifier)
            &&  (pEvent->modifiers() & Qt::ControlModifier)
            && !(pEvent->modifiers() & Qt::AltModifier)
            && !(pEvent->modifiers() & Qt::MetaModifier)
            &&  (pEvent->key() == Qt::Key_0))
            zoomTo(0);
        else
            // Default handling of the event

            QsciScintilla::keyPressEvent(pEvent);
    }
}

//==============================================================================

void QScintillaWidget::wheelEvent(QWheelEvent *pEvent)
{
    // Increasing/decrease the font size, if needed

    if (pEvent->modifiers() == Qt::ControlModifier) {
        int delta = pEvent->delta();

        if (delta > 0)
            zoomIn();
        else if (delta < 0)
            zoomOut();

        pEvent->accept();
    } else {
        QsciScintilla::wheelEvent(pEvent);
    }
}

//==============================================================================

void QScintillaWidget::updateUi()
{
    // Update our editing mode, if needed

    bool newOverwriteMode = overwriteMode();

    if (   (newOverwriteMode != mOverwriteMode)
        || mEditingModeWidget->text().isEmpty()) {
        mOverwriteMode = newOverwriteMode;

        mEditingModeWidget->setText(mOverwriteMode?"OVR":"INS");
    }
}

//==============================================================================

void QScintillaWidget::updateCanUndoAndCanRedo()
{
    // Check whether undoing/redoing is possible

    bool newCanUndo = isUndoAvailable();
    bool newCanRedo = isRedoAvailable();

    if (newCanUndo != mCanUndo) {
        mCanUndo = newCanUndo;

        emit canUndo(mCanUndo);
    }

    if (newCanRedo != mCanRedo) {
        mCanRedo = newCanRedo;

        emit canRedo(mCanRedo);
    }
}

//==============================================================================

void QScintillaWidget::checkCanSelectAll()
{
    // Check whether we can select all the text

    bool newCanSelectAll = text().size() && selectedText().compare(text());

    if (newCanSelectAll != mCanSelectAll) {
        mCanSelectAll = newCanSelectAll;

        emit canSelectAll(mCanSelectAll);
    }
}

//==============================================================================

void QScintillaWidget::updateColors()
{
    // Compute and set the background colour of our caret line

    static const qreal Threshold = 0.875;

    QColor caretLineBackgroundColor = Core::highlightColor();

    qreal r = caretLineBackgroundColor.redF();
    qreal g = caretLineBackgroundColor.greenF();
    qreal b = caretLineBackgroundColor.blueF();

    while ((r < Threshold) || (g < Threshold) || (b < Threshold)) {
        r = 0.5*(r+1.0);
        g = 0.5*(g+1.0);
        b = 0.5*(b+1.0);
    }

    setCaretLineBackgroundColor(qRgba(r*255, g*255, b*255, caretLineBackgroundColor.alpha()));
}

//==============================================================================

void QScintillaWidget::cursorPositionChanged(const int &pLine,
                                             const int &pColumn)
{
    // Update our cursor position

    mCursorPositionWidget->setText(QString("Line: %1, Col: %2").arg(QString::number(pLine+1),
                                                                    QString::number(pColumn+1)));
}

//==============================================================================

}   // namespace QScintillaSupport
}   // namespace OpenCOR

//==============================================================================
// End of file
//==============================================================================
<commit_msg>Some minor cleaning up.<commit_after>/*******************************************************************************

Licensed to the OpenCOR team under one or more contributor license agreements.
See the NOTICE.txt file distributed with this work for additional information
regarding copyright ownership. The OpenCOR team licenses this file to you under
the Apache License, Version 2.0 (the "License"); you may not use this file
except in compliance with the License. You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied. See the License for the
specific language governing permissions and limitations under the License.

*******************************************************************************/

//==============================================================================
// QScintillaWidget class
//==============================================================================

#include "filemanager.h"
#include "guiutils.h"
#include "qscintillawidget.h"

//==============================================================================

#include <Qt>

//==============================================================================

#include <QDragEnterEvent>
#include <QLabel>
#include <QMenu>
#include <QMimeData>

//==============================================================================

#include "Qsci/qscilexer.h"

//==============================================================================

namespace OpenCOR {
namespace QScintillaSupport {

//==============================================================================

QScintillaWidget::QScintillaWidget(const QString &pContents,
                                   const bool &pReadOnly,
                                   QsciLexer *pLexer, QWidget *pParent) :
    QsciScintilla(pParent),
    mCanUndo(false),
    mCanRedo(false),
    mCanSelectAll(false),
    mOverwriteMode(false)
{
    // Remove the frame around our Scintilla editor

    setFrameShape(QFrame::NoFrame);

    // Remove the margin number in our Scintilla editor

    setMarginWidth(SC_MARGIN_NUMBER, 0);

    // Associate a lexer to our Scintilla editor, should one be provided
    // Note: the default font family and size come from Qt Creator...

#if defined(Q_OS_WIN)
    mFont = QFont("Courier", 10);
#elif defined(Q_OS_LINUX)
    mFont = QFont("Monospace", 9);
#elif defined(Q_OS_MAC)
    mFont = QFont("Monaco", 12);
#else
    #error Unsupported platform
#endif

    if (pLexer) {
        // A lexer was provided, so specify its fonts and associate it with our
        // Scintilla editor

        pLexer->setFont(mFont);

        setLexer(pLexer);

        // Specify the type of tree folding to be used. Some lexers may indeed
        // use that feature, so...

        setFolding(QsciScintilla::BoxedTreeFoldStyle);
    } else {
        // No lexer was provided, so simply specify a default font family and
        // size for our Scintilla editor

        setFont(mFont);
    }

    // Set the contents of our Scintilla editor and its read-only property

    setContents(pContents);
    setReadOnly(pReadOnly);

    // Show the caret line

    setCaretLineVisible(true);

    // Force the use of UNIX EOL mode
    // Note: by default QScintilla will use EolWindows on Windows and EolUnix on
    //       Linux and OS X. However, the fact that it uses EolWindows on
    //       Windows can cause problems on that platform with files not using a
    //       a Windows EOL mode, so...

    setEolMode(EolUnix);

    // Initialise our colours by 'updating' them

    updateColors();

    // Clear some key mappings inherited from QsciScintilla
    // Note #1: indeed, QsciScintilla handles some shortcuts (e.g. Ctrl+L),
    //          which we don't want to see handled (e.g. Ctrl+L is used by
    //          QsciScintilla to delete the current line while OpenCOR uses it
    //          to (un)lock the current file), so...
    // Note #2: even though we are clearing those key mappings, we must also
    //          bypass QsciScintilla's handling of event() (see below). Indeed,
    //          not to do so would mean that if, for example, the user was to
    //          press Ctrl+L, then nothing would happen while we would have
    //          expected the current file to be (un)locked...

    SendScintilla(SCI_CLEARCMDKEY, (SCMOD_CTRL << 16)+'D');
    SendScintilla(SCI_CLEARCMDKEY, (SCMOD_CTRL << 16)+'L');
    SendScintilla(SCI_CLEARCMDKEY, (SCMOD_CTRL << 16)+(SCMOD_SHIFT << 16)+'L');

    // Empty context menu by default

    mContextMenu = new QMenu(this);

    // Create our two labels to show our cursor position and editing mode

    mCursorPositionWidget = new QLabel(this);
    mEditingModeWidget = new QLabel(this);

    // Keep track of the change to the UI

    connect(this, SIGNAL(SCN_UPDATEUI(int)),
            this, SLOT(updateUi()));

    // Keep track of whether we can undo/redo

    connect(this, SIGNAL(textChanged()),
            this, SLOT(updateCanUndoAndCanRedo()));

    // Keep track of changes to our editor that may affect our ability to select
    // all of its text
    // Note: we use the SCN_MODIFIED() signal rather than the textChanged()
    //       signal since the latter is only emitted when inserting or deleting
    //       some text...

    connect(this, SIGNAL(selectionChanged()),
            this, SLOT(checkCanSelectAll()));
    connect(this, SIGNAL(SCN_MODIFIED(int, int, const char *, int, int, int, int, int, int, int)),
            this, SLOT(checkCanSelectAll()));

    // Keep track of the change in the cursor position

    connect(this, SIGNAL(cursorPositionChanged(int, int)),
            this, SLOT(cursorPositionChanged(const int &, const int &)));
}

//==============================================================================

QMenu * QScintillaWidget::contextMenu() const
{
    // Return our context menu

    return mContextMenu;
}

//==============================================================================

void QScintillaWidget::setContextMenu(const QList<QAction *> &pContextMenuActions)
{
    // Set our context menu

    mContextMenu->clear();

    foreach (QAction *action, pContextMenuActions)
        mContextMenu->addAction(action);
}

//==============================================================================

int QScintillaWidget::currentPosition() const
{
    // Return our current position

    return SendScintilla(SCI_GETCURRENTPOS);
}

//==============================================================================

QString QScintillaWidget::contents() const
{
    // Return our contents

    return text();
}

//==============================================================================

void QScintillaWidget::setContents(const QString &pContents)
{
    // Set our contents

    setText(pContents);
}

//==============================================================================

int QScintillaWidget::contentsSize() const
{
    // Return the size of our contents

    return SendScintilla(SCI_GETLENGTH);
}

//==============================================================================

QString QScintillaWidget::textInRange(const int &pStartRange,
                                      const int &pEndRange) const
{
    // Retrieve and return the text in the given range, making sure that the
    // given range makes sense

    int maxRange = contentsSize();

    if (   (pStartRange < 0) || (pStartRange >= maxRange)
        || (pEndRange < 0) || (pEndRange >= maxRange)
        || (pStartRange >= pEndRange))
        return QString();

    char *text = new char[pEndRange-pStartRange+1];

    SendScintilla(SCI_GETTEXTRANGE, pStartRange, pEndRange, text);

    QString res = QString(text);

    delete[] text;

    return res;
}

//==============================================================================

int QScintillaWidget::findTextInRange(const int &pStartRange,
                                      const int &pEndRange,
                                      const QString &pText,
                                      const bool &pCaseSensitive) const
{
    // Find and return the position, if any, of the given text within the given
    // range

    SendScintilla(SCI_SETSEARCHFLAGS, pCaseSensitive?SCFIND_MATCHCASE:0);

    SendScintilla(SCI_SETTARGETSTART, pStartRange);
    SendScintilla(SCI_SETTARGETEND, pEndRange);

    QByteArray text = pText.toUtf8();

    return SendScintilla(SCI_SEARCHINTARGET, text.length(), text.constData());
}

//==============================================================================

bool QScintillaWidget::isSelectAllAvailable() const
{
    // Return whether we can select all the text

    return mCanSelectAll;
}

//==============================================================================

void QScintillaWidget::del()
{
    // Delete the selected text, if any

    SendScintilla(QsciScintillaBase::SCI_CLEAR);
}

//==============================================================================

void QScintillaWidget::resetUndoHistory()
{
    // Reset our undo history

    SendScintilla(SCI_EMPTYUNDOBUFFER);

    mCanUndo = false;
    mCanRedo = false;
}

//==============================================================================

QLabel * QScintillaWidget::cursorPositionWidget() const
{
    // Return our cursort position widget

    return mCursorPositionWidget;
}

//==============================================================================

QLabel * QScintillaWidget::editingModeWidget() const
{
    // Return our editing mode widget

    return mEditingModeWidget;
}

//==============================================================================

QString QScintillaWidget::eolString() const
{
    // Return the end of line we use

    switch (eolMode()) {
    case EolUnix:
        return "\n";
    case EolMac:
        return "\r";
    default:   // EolWindows
        return "\r\n";
    }
}

//==============================================================================

void QScintillaWidget::setBackgroundColor(const int &pStyle,
                                          const QColor &pBackgroundColor)
{
    // Set the background color for the given style

    SendScintilla(QsciScintillaBase::SCI_STYLESETBACK, pStyle, pBackgroundColor);
}

//==============================================================================

void QScintillaWidget::setForegroundColor(const int &pStyle,
                                          const QColor &pForegroundColor)
{
    // Set the foreground color for the given style

    SendScintilla(QsciScintillaBase::SCI_STYLESETFORE, pStyle, pForegroundColor);
}

//==============================================================================

int QScintillaWidget::zoomLevel() const
{
    // Return our zoom level

    return SendScintilla(QsciScintillaBase::SCI_GETZOOM);
}

//==============================================================================

void QScintillaWidget::changeEvent(QEvent *pEvent)
{
    // Default handling of the event

    QsciScintilla::changeEvent(pEvent);

    // Check whether the palette has changed and if so then update our colors

    if (pEvent->type() == QEvent::PaletteChange)
        updateColors();
}

//==============================================================================

void QScintillaWidget::contextMenuEvent(QContextMenuEvent *pEvent)
{
    // Show our context menu or QsciScintilla's one, if we don't have one

    if (mContextMenu->isEmpty())
        QsciScintilla::contextMenuEvent(pEvent);
    else
        mContextMenu->exec(pEvent->globalPos());
}

//==============================================================================

void QScintillaWidget::dragEnterEvent(QDragEnterEvent *pEvent)
{
    // Accept the proposed action for the event, but only if we are not dropping
    // URIs
    // Note: this is not (currently?) needed on Windows and OS X, but if we
    //       don't have that check on Linux, then to drop some files on our
    //       Scintilla editor will result in the text/plain version of the data
    //       (e.g. file:///home/me/myFile) to be inserted in the text, so...

    if (!pEvent->mimeData()->hasFormat(Core::FileSystemMimeType))
        pEvent->acceptProposedAction();
    else
        pEvent->ignore();
}

//==============================================================================

bool QScintillaWidget::event(QEvent *pEvent)
{
    // Bypass QsciScintilla's handling of event()
    // Note: see the note on the clearing of some key mappings in the contructor
    //       above...

    return QsciScintillaBase::event(pEvent);
}

//==============================================================================

void QScintillaWidget::keyPressEvent(QKeyEvent *pEvent)
{
    // Let people know that a key has been pressed

    bool handled = false;

    emit keyPressed(pEvent, handled);

    // Carry on as normal, if the event wasn't handled

    if (handled) {
        // Accept the event

        pEvent->accept();
    } else {
        // Reset the font size, if needed

        if (   !(pEvent->modifiers() & Qt::ShiftModifier)
            &&  (pEvent->modifiers() & Qt::ControlModifier)
            && !(pEvent->modifiers() & Qt::AltModifier)
            && !(pEvent->modifiers() & Qt::MetaModifier)
            &&  (pEvent->key() == Qt::Key_0)) {
            zoomTo(0);

            // Accept the event

            pEvent->accept();
        } else {
            // Default handling of the event

            QsciScintilla::keyPressEvent(pEvent);
        }
    }
}

//==============================================================================

void QScintillaWidget::wheelEvent(QWheelEvent *pEvent)
{
    // Increasing/decrease the font size, if needed

    if (pEvent->modifiers() == Qt::ControlModifier) {
        int delta = pEvent->delta();

        if (delta > 0)
            zoomIn();
        else if (delta < 0)
            zoomOut();

        pEvent->accept();
    } else {
        QsciScintilla::wheelEvent(pEvent);
    }
}

//==============================================================================

void QScintillaWidget::updateUi()
{
    // Update our editing mode, if needed

    bool newOverwriteMode = overwriteMode();

    if (   (newOverwriteMode != mOverwriteMode)
        || mEditingModeWidget->text().isEmpty()) {
        mOverwriteMode = newOverwriteMode;

        mEditingModeWidget->setText(mOverwriteMode?"OVR":"INS");
    }
}

//==============================================================================

void QScintillaWidget::updateCanUndoAndCanRedo()
{
    // Check whether undoing/redoing is possible

    bool newCanUndo = isUndoAvailable();
    bool newCanRedo = isRedoAvailable();

    if (newCanUndo != mCanUndo) {
        mCanUndo = newCanUndo;

        emit canUndo(mCanUndo);
    }

    if (newCanRedo != mCanRedo) {
        mCanRedo = newCanRedo;

        emit canRedo(mCanRedo);
    }
}

//==============================================================================

void QScintillaWidget::checkCanSelectAll()
{
    // Check whether we can select all the text

    bool newCanSelectAll = text().size() && selectedText().compare(text());

    if (newCanSelectAll != mCanSelectAll) {
        mCanSelectAll = newCanSelectAll;

        emit canSelectAll(mCanSelectAll);
    }
}

//==============================================================================

void QScintillaWidget::updateColors()
{
    // Compute and set the background colour of our caret line

    static const qreal Threshold = 0.875;

    QColor caretLineBackgroundColor = Core::highlightColor();

    qreal r = caretLineBackgroundColor.redF();
    qreal g = caretLineBackgroundColor.greenF();
    qreal b = caretLineBackgroundColor.blueF();

    while ((r < Threshold) || (g < Threshold) || (b < Threshold)) {
        r = 0.5*(r+1.0);
        g = 0.5*(g+1.0);
        b = 0.5*(b+1.0);
    }

    setCaretLineBackgroundColor(qRgba(r*255, g*255, b*255, caretLineBackgroundColor.alpha()));
}

//==============================================================================

void QScintillaWidget::cursorPositionChanged(const int &pLine,
                                             const int &pColumn)
{
    // Update our cursor position

    mCursorPositionWidget->setText(QString("Line: %1, Col: %2").arg(QString::number(pLine+1),
                                                                    QString::number(pColumn+1)));
}

//==============================================================================

}   // namespace QScintillaSupport
}   // namespace OpenCOR

//==============================================================================
// End of file
//==============================================================================
<|endoftext|>


<commit_before>// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include <vespa/document/repo/documenttyperepo.h>
#include <vespa/vdslib/container/parameters.h>
#include <vespa/vespalib/gtest/gtest.h>

using document::DocumentTypeRepo;
using namespace vdslib;

TEST(ParametersTest, test_parameters)
{
    Parameters par;
    par.set("fast", "overture");
    par.set("overture", "yahoo");
    par.set("number", 6);
    par.set("long", 8589934590L);
    par.set("double", 0.25);
    std::unique_ptr<document::ByteBuffer> buffer(par.serialize());

    buffer->flip();
    DocumentTypeRepo repo;
    Parameters par2(repo, *buffer);

    EXPECT_EQ(vespalib::stringref("overture"), par2.get("fast"));
    EXPECT_EQ(vespalib::stringref("yahoo"), par2.get("overture"));
    std::string stringDefault = "wayne corp";
    int numberDefault = 123;
    long longDefault = 456;
    double doubleDefault = 0.5;
    EXPECT_EQ(6, par2.get("number", numberDefault));
    EXPECT_EQ(8589934590L, par2.get("long", longDefault));
    EXPECT_DOUBLE_EQ(0.25, par2.get("double", doubleDefault));

    EXPECT_EQ(stringDefault, par2.get("nonexistingstring", stringDefault));
    EXPECT_EQ(numberDefault, par2.get("nonexistingnumber", numberDefault));
    EXPECT_EQ(longDefault,   par2.get("nonexistinglong", longDefault));
    EXPECT_EQ(doubleDefault, par2.get("nonexistingdouble", doubleDefault));
}
<commit_msg>Use int64_t for vdslib::Parameters overload.<commit_after>// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include <vespa/document/repo/documenttyperepo.h>
#include <vespa/vdslib/container/parameters.h>
#include <vespa/vespalib/gtest/gtest.h>

using document::DocumentTypeRepo;
using namespace vdslib;

TEST(ParametersTest, test_parameters)
{
    Parameters par;
    par.set("fast", "overture");
    par.set("overture", "yahoo");
    par.set("number", 6);
    par.set("int64_t", INT64_C(8589934590));
    par.set("double", 0.25);
    std::unique_ptr<document::ByteBuffer> buffer(par.serialize());

    buffer->flip();
    DocumentTypeRepo repo;
    Parameters par2(repo, *buffer);

    EXPECT_EQ(vespalib::stringref("overture"), par2.get("fast"));
    EXPECT_EQ(vespalib::stringref("yahoo"), par2.get("overture"));
    std::string stringDefault = "wayne corp";
    int numberDefault = 123;
    int64_t int64Default = 456;
    double doubleDefault = 0.5;
    EXPECT_EQ(6, par2.get("number", numberDefault));
    EXPECT_EQ(INT64_C(8589934590), par2.get("int64_t", int64Default));
    EXPECT_DOUBLE_EQ(0.25, par2.get("double", doubleDefault));

    EXPECT_EQ(stringDefault, par2.get("nonexistingstring", stringDefault));
    EXPECT_EQ(numberDefault, par2.get("nonexistingnumber", numberDefault));
    EXPECT_EQ(int64Default,  par2.get("nonexistingint64_t", int64Default));
    EXPECT_EQ(doubleDefault, par2.get("nonexistingdouble", doubleDefault));
}
<|endoftext|>


<commit_before>#ifndef BFC_COMBINE_INC_VISITOR_HPP
#define BFC_COMBINE_INC_VISITOR_HPP

#include "ast/mod.hpp"
#include "ast/base.hpp"
#include "test_visitor.hpp"
#include "types.h"

namespace bfc {
namespace ast {

class combine_inc_visitor : public opt_seq_base_visitor {

public:

    enum node_type {
        ADD = 0,
        SUB
    };

    status visit(add &node) {
        // discard any zero value node
        if (node.value() == 0) {
            return CONTINUE;
        }
        // Only attempt to combine if there is a previous node
        if (!opt_seq.empty()) {
            // try to combine with the previous node if possible
            try_combine_inc_visitor v(node.offset(), node.value(), true);
            if (opt_seq.back().accept(v) == CONTINUE) {
                opt_seq.pop_back();
                // discard the combined node if it is 0
                if (v.new_value() != 0) {
                    if (v.type() == ADD) {
                        opt_seq.emplace_back(new add(node.loc(), node.offset(), v.new_value()));
                    } else {
                        opt_seq.emplace_back(new sub(node.loc(), node.offset(), v.new_value()));
                    }
                }
                return CONTINUE;
            }
        }
        // else make node copy
        return opt_seq_base_visitor::handle_add(node);
    }

    status visit(const add &node) {
        // discard any zero value node
        if (node.value() == 0) {
            return CONTINUE;
        }
        // Only attempt to combine if there is a previous node
        if (!opt_seq.empty()) {
            // try to combine with the previous node if possible
            try_combine_inc_visitor v(node.offset(), node.value(), true);
            if (opt_seq.back().accept(v) == CONTINUE) {
                opt_seq.pop_back();
                // discard the combined node if it is 0
                if (v.new_value() != 0) {
                    if (v.type() == ADD) {
                        opt_seq.emplace_back(new add(node.loc(), node.offset(), v.new_value()));
                    } else {
                        opt_seq.emplace_back(new sub(node.loc(), node.offset(), v.new_value()));
                    }
                }
                return CONTINUE;
            }
        }
        // else make node copy
        return opt_seq_base_visitor::handle_add(node);
    }

    status visit(sub &node) {
        // discard any zero value node
        if (node.value() == 0) {
            return CONTINUE;
        }
        // Only attempt to combine if there is a previous node
        if (!opt_seq.empty()) {
            // try to combine with the previous node if possible
            try_combine_inc_visitor v(node.offset(), node.value(), false);
            if (opt_seq.back().accept(v) == CONTINUE) {
                opt_seq.pop_back();
                // discard the combined node if it is 0
                if (v.new_value() != 0) {
                    if (v.type() == ADD) {
                        opt_seq.emplace_back(new add(node.loc(), node.offset(), v.new_value()));
                    } else {
                        opt_seq.emplace_back(new sub(node.loc(), node.offset(), v.new_value()));
                    }
                    return CONTINUE;
                }
            }
        }
        // else make node copy
        return opt_seq_base_visitor::handle_sub(node);
    }

    status visit(const sub &node) {
        // discard any zero value node
        if (node.value() == 0) {
            return CONTINUE;
        }
        // Only attempt to combine if there is a previous node
        if (!opt_seq.empty()) {
            // try to combine with the previous node if possible
            try_combine_inc_visitor v(node.offset(), node.value(), false);
            if (opt_seq.back().accept(v) == CONTINUE) {
                opt_seq.pop_back();
                // discard the combined node if it is 0
                if (v.new_value() != 0) {
                    if (v.type() == ADD) {
                        opt_seq.emplace_back(new add(node.loc(), node.offset(), v.new_value()));
                    } else {
                        opt_seq.emplace_back(new sub(node.loc(), node.offset(), v.new_value()));
                    }
                    return CONTINUE;
                }
                return CONTINUE;
            }
        }
        // else make node copy
        return opt_seq_base_visitor::handle_sub(node);
    }

private:

    class try_combine_inc_visitor : public test_visitor {

        public:
            try_combine_inc_visitor(ptrdiff_t offset, bf_value val, bool isAdd) :
                next_off(offset), next_val(val), isAdd(isAdd) {}

            bf_value new_value() {
                return new_val;
            }

            node_type type() {
                return this_type;
            }

            status visit(add &node) {
                if (node.offset() != next_off) {
                    return BREAK;
                }
                new_val = node.value();
                isAdd ? new_val += next_val : new_val -= next_val;
                type = ADD;
                return CONTINUE;
            }

            status visit(const add &node) {
                if (node.offset() != next_off) {
                    return BREAK;
                }
                new_val = node.value();
                isAdd ? new_val += next_val : new_val -= next_val;
                type = ADD;
                return CONTINUE;
            }

            status visit(sub &node) {
                if (node.offset() != next_off) {
                    return BREAK;
                }
                new_val = node.value();
                isAdd ? new_val -= next_val : new_val += next_val;
                type = SUB;
                return CONTINUE;
            }

            status visit(const sub &node) {
                if (node.offset() != next_off) {
                    return BREAK;
                }
                new_val = node.value();
                isAdd ? new_val -= next_val : new_val += next_val;
                type = SUB;
                return CONTINUE;
            }

        private:
            bool isAdd;
            ptrdiff_t next_off;
            bf_value next_val;
            bf_value new_val;
            node_type this_type;
    };

};

}
}

#endif /* !BFC_COMBINE_INC_VISITOR_HPP */
<commit_msg>Extra var changes<commit_after>#ifndef BFC_COMBINE_INC_VISITOR_HPP
#define BFC_COMBINE_INC_VISITOR_HPP

#include "ast/mod.hpp"
#include "ast/base.hpp"
#include "test_visitor.hpp"
#include "types.h"

namespace bfc {
namespace ast {

class combine_inc_visitor : public opt_seq_base_visitor {

public:

    enum node_type {
        ADD = 0,
        SUB
    };

    status visit(add &node) {
        // discard any zero value node
        if (node.value() == 0) {
            return CONTINUE;
        }
        // Only attempt to combine if there is a previous node
        if (!opt_seq.empty()) {
            // try to combine with the previous node if possible
            try_combine_inc_visitor v(node.offset(), node.value(), true);
            if (opt_seq.back().accept(v) == CONTINUE) {
                opt_seq.pop_back();
                // discard the combined node if it is 0
                if (v.new_value() != 0) {
                    if (v.type() == ADD) {
                        opt_seq.emplace_back(new add(node.loc(), node.offset(), v.new_value()));
                    } else {
                        opt_seq.emplace_back(new sub(node.loc(), node.offset(), v.new_value()));
                    }
                }
                return CONTINUE;
            }
        }
        // else make node copy
        return opt_seq_base_visitor::handle_add(node);
    }

    status visit(const add &node) {
        // discard any zero value node
        if (node.value() == 0) {
            return CONTINUE;
        }
        // Only attempt to combine if there is a previous node
        if (!opt_seq.empty()) {
            // try to combine with the previous node if possible
            try_combine_inc_visitor v(node.offset(), node.value(), true);
            if (opt_seq.back().accept(v) == CONTINUE) {
                opt_seq.pop_back();
                // discard the combined node if it is 0
                if (v.new_value() != 0) {
                    if (v.type() == ADD) {
                        opt_seq.emplace_back(new add(node.loc(), node.offset(), v.new_value()));
                    } else {
                        opt_seq.emplace_back(new sub(node.loc(), node.offset(), v.new_value()));
                    }
                }
                return CONTINUE;
            }
        }
        // else make node copy
        return opt_seq_base_visitor::handle_add(node);
    }

    status visit(sub &node) {
        // discard any zero value node
        if (node.value() == 0) {
            return CONTINUE;
        }
        // Only attempt to combine if there is a previous node
        if (!opt_seq.empty()) {
            // try to combine with the previous node if possible
            try_combine_inc_visitor v(node.offset(), node.value(), false);
            if (opt_seq.back().accept(v) == CONTINUE) {
                opt_seq.pop_back();
                // discard the combined node if it is 0
                if (v.new_value() != 0) {
                    if (v.type() == ADD) {
                        opt_seq.emplace_back(new add(node.loc(), node.offset(), v.new_value()));
                    } else {
                        opt_seq.emplace_back(new sub(node.loc(), node.offset(), v.new_value()));
                    }
                    return CONTINUE;
                }
            }
        }
        // else make node copy
        return opt_seq_base_visitor::handle_sub(node);
    }

    status visit(const sub &node) {
        // discard any zero value node
        if (node.value() == 0) {
            return CONTINUE;
        }
        // Only attempt to combine if there is a previous node
        if (!opt_seq.empty()) {
            // try to combine with the previous node if possible
            try_combine_inc_visitor v(node.offset(), node.value(), false);
            if (opt_seq.back().accept(v) == CONTINUE) {
                opt_seq.pop_back();
                // discard the combined node if it is 0
                if (v.new_value() != 0) {
                    if (v.type() == ADD) {
                        opt_seq.emplace_back(new add(node.loc(), node.offset(), v.new_value()));
                    } else {
                        opt_seq.emplace_back(new sub(node.loc(), node.offset(), v.new_value()));
                    }
                    return CONTINUE;
                }
                return CONTINUE;
            }
        }
        // else make node copy
        return opt_seq_base_visitor::handle_sub(node);
    }

private:

    class try_combine_inc_visitor : public test_visitor {

        public:
            try_combine_inc_visitor(ptrdiff_t offset, bf_value val, bool isAdd) :
                next_off(offset), next_val(val), isAdd(isAdd) {}

            bf_value new_value() {
                return new_val;
            }

            node_type type() {
                return this_type;
            }

            status visit(add &node) {
                if (node.offset() != next_off) {
                    return BREAK;
                }
                new_val = node.value();
                isAdd ? new_val += next_val : new_val -= next_val;
                this_type = ADD;
                return CONTINUE;
            }

            status visit(const add &node) {
                if (node.offset() != next_off) {
                    return BREAK;
                }
                new_val = node.value();
                isAdd ? new_val += next_val : new_val -= next_val;
                this_type = ADD;
                return CONTINUE;
            }

            status visit(sub &node) {
                if (node.offset() != next_off) {
                    return BREAK;
                }
                new_val = node.value();
                isAdd ? new_val -= next_val : new_val += next_val;
                this_type = SUB;
                return CONTINUE;
            }

            status visit(const sub &node) {
                if (node.offset() != next_off) {
                    return BREAK;
                }
                new_val = node.value();
                isAdd ? new_val -= next_val : new_val += next_val;
                this_type = SUB;
                return CONTINUE;
            }

        private:
            bool isAdd;
            ptrdiff_t next_off;
            bf_value next_val;
            bf_value new_val;
            node_type this_type;
    };

};

}
}

#endif /* !BFC_COMBINE_INC_VISITOR_HPP */
<|endoftext|>


<commit_before>#ifndef MJOLNIR_GLOBAL_PARAMETER_LIST_HPP
#define MJOLNIR_GLOBAL_PARAMETER_LIST_HPP
#include <mjolnir/core/ExclusionList.hpp>
#include <mjolnir/core/System.hpp>
#include <vector>
#include <map>

namespace mjolnir
{
//
// GlobalParameterList contains list of per-particle parameter used in a pair
// interaction and a combination rule.
//
template<typename traitsT, typename ruleT>
class GlobalParameterList
{
  public:
    using traits_type           = traitsT;
    using combination_rule_type = ruleT;
    using parameter_type        = typename rule_type::parameter_type;
    using pair_parameter_type   = typename rule_type::pair_parameter_type;
    using system_type           = System<traits_type>;
    using container_type        = std::vector<parameter_type>;

    // topology stuff
    using topology_type        = Topology;
    using molecule_id_type     = typename topology_type::molecule_id_type;
    using group_id_type        = typename topology_type::group_id_type;
    using connection_kind_type = typename topology_type::connection_kind_type;
    using ignore_molecule_type = IgnoreMolecule<molecule_id_type>;
    using ignore_group_type    = IgnoreGroup   <group_id_type>;
    using exclusion_list_type  = ExclusionList <traits_type>;

  public:

    GlobalParameterList(combination_rule_type rule,
        const std::map<connection_kind_type, std::size_t>& exclusions,
        ignore_molecule_type ignore_mol, ignore_group_type ignore_grp,
        parameter_type default_parameter)
      : rule_(std::move(rule)),
        exclusion_list_(exclusions, std::move(ignore_mol), std::move(ignore_grp))
    {}

    pair_parameter_type prepare_params(std::size_t i, std::size_t j) const noexcept
    {
        return rule_(i, j);
    }

    // These value

    template<typename PotentialT>
    void initialize(const system_type& sys, const topology_type& topol,
                    const PotentialT& pot) noexcept
    {
        MJOLNIR_GET_DEFAULT_LOGGER();
        MJOLNIR_LOG_FUNCTION();

        this->update(sys, topol, pot);
        return;
    }

    template<typename PotentialT>
    void update(const system_type& sys, const topology_type& topol,
                const PotentialT& pot) noexcept
    {
        MJOLNIR_GET_DEFAULT_LOGGER();
        MJOLNIR_LOG_FUNCTION();

        this->max_cutoff_length_ = rule_.max_cutoff_length(pot);
        this->exclusion_list_.make(sys, topol);
        return;
    }

    real_type max_cutoff_length() const noexcept {return max_cutoff_length_;}

    // -----------------------------------------------------------------------
    // Here, the default implementation uses Newton's 3rd law to reduce
    // calculation. For an interacting pair (i, j), forces applied to i and j
    // are equal in magnitude and opposite in direction. So, if a pair (i, j) is
    // listed, (j, i) is not needed.
    //     See implementation of VerletList, CellList and GlobalPairInteraction
    // for more details about the usage of these functions.
    //
    bool has_interaction(const std::size_t i, const std::size_t j) const noexcept
    {
        // if not excluded, the pair has interaction.
        return (i < j) && !exclusion_list_.is_excluded(i, j);
    }
    std::vector<std::size_t> const& participants() const noexcept
    {
        return rule_.participants();
    }
    range<typename std::vector<std::size_t>::const_iterator>
    leading_participants() const noexcept
    {
        const auto& ps = rule_.participants();
        return make_range(ps.begin(), std::prev(ps.end()));
    }
    range<typename std::vector<std::size_t>::const_iterator>
    possible_partners_of(const std::size_t participant_idx,
                         const std::size_t /*particle_idx*/) const noexcept
    {
        return make_range(ps.begin() + participant_idx + 1, ps.end());
    }

    // ------------------------------------------------------------------------
    // the following accessers would be used in tests.

    exclusion_list_type const& exclusion_list() const noexcept
    {
        return exclusion_list_;
    }

    container_type&       parameters()       noexcept {return rule_.parameters();}
    container_type const& parameters() const noexcept {return rule_.parameters();}

    combination_rule_type&       rule()       noexcept {return rule_;}
    combination_rule_type const& rule() const noexcept {return rule_;}

  private:

    real_type                max_cutoff_length_;
    combination_rule_type    rule_;
    exclusion_list_type      exclusion_list_;
};

// ----------------------------------------------------------------------------
// widely-used parameter combination rules
//
// CombinationRules should provide the following two functionality.
// 1. combination rule
// 2. the pair parameter that provides the maximum cutoff distance
//
// The second one depends on the functional form of the potential, so the
// comparator used to find the max cutoff distance is given by the potential
// class via get_parameter_comparator().
//     To avoid O(n^2) search while finding the maximum cutoff distance, it
// linearly search the parameters using the comparator. It means that, the
// combination of the maximum parameters (two identical, maximum parameters)
// should give the maximum cutoff distance. This is automatically satisfied
// if you are using Lorentz-Berthelot rule or some other variant of it. If
// you are using combination table, the comparator directly compare the
// pair parameters. So, the comparator should be able to be used for both
// per-particle parameters and pair parameters.

template<typename realT>
struct LorentzBerthelotRule
{
  public:
    using real_type           = realT;
    using parameter_type      = std::pair<real_type, real_type>;
    using pair_parameter_type = std::pair<real_type, real_type>;
    using container_type      = std::vector<parameter_type>;

  public:

    explicit LorentzBerthelotRule(
        const std::vector<std::pair<std::size_t, parameter_type>>& parameters)
    {
        this->parameters_  .reserve(parameters.size());
        this->participants_.reserve(parameters.size());
        for(const auto& idxp : parameters)
        {
            const auto idx = idxp.first;
            this->participants_.push_back(idx);
            if(this->parameters_.size() <= idx)
            {
                this->parameters_.resize(idx+1, default_parameter);
            }
            this->parameters_.at(idx) = idxp.second;
        }
    }

    // calculate combination of parameters
    pair_parameter_type operator()(const std::size_t i, std::size_t j) const noexcept
    {
        const auto& para1 = parameters_[i];
        const auto& para2 = parameters_[j];

        const auto sgm1 = para1.first;
        const auto eps1 = para1.second;
        const auto sgm2 = para2.first;
        const auto eps2 = para2.second;

        return std::make_pair((sgm1 + sgm2) / 2,
                             ((eps1 == eps2) ? eps1 : std::sqrt(eps1 * eps2)));
    }

    // This function is for cutoff distance
    template<typename PotentialT>
    real_type max_cutoff_length(const PotentialT& pot) const
    {
        if(parameters_.empty())
        {
            return std::numeric_limits<real_type>::infinity();
        }
        const auto max_iter = std::max_element(
                parameters_.begin(), parameters_.end(), pot.get_parameter_comparator());

        return PotentialT(*max_iter).cutoff();
    }

    // accessors for testing

    container_type&       parameters()       noexcept {return parameters_;}
    container_type const& parameters() const noexcept {return parameters_;}

    std::vector<std::size_t>&       participants()       noexcept {return participants_;}
    std::vector<std::size_t> const& participants() const noexcept {return participants_;}

  private:

    std::vector<std::size_t> participants_;
    container_type           parameters_;
};

template<typename pair_parameterT>
struct CombinationTable
{
  public:
    using parameter_type      = std::string;
    using pair_parameter_type = parameterT;
    using container_type      = std::vector<parameter_type>;
    using table_type          = std::unordered_map<std::string, pair_parameter_type>;

  public:

    CombinationTable(const std::vector<std::pair<std::size_t, parameter_type>>& parameters,
                     table_type&& table)
        : table_(std::move(table))
    {
        this->parameters_  .reserve(parameters.size());
        this->participants_.reserve(parameters.size());
        for(const auto& idxp : parameters)
        {
            const auto idx = idxp.first;
            this->participants_.push_back(idx);
            if(this->parameters_.size() <= idx)
            {
                this->parameters_.resize(idx+1, default_parameter);
            }
            this->parameters_.at(idx) = idxp.second;
        }
    }

    // Since it concats the names, it is better to keep parameter name short.
    pair_parameter_type
    operator()(const std::size_t i, const std::size_t j) const noexcept
    {
        const auto& para1 = parameters_[i];
        const auto& para2 = parameters_[j];
        return table_[para1 + para2];
    }

    // This function is for cutoff distance
    template<typename PotentialT>
    real_type max_cutoff_length(const PotentialT& pot) const
    {
        if(parameters_.empty())
        {
            return std::numeric_limits<real_type>::infinity();
        }

        using value_type = typename container_type::value_type; // kv-pair
        const auto max_iter = std::max_element(table_.begin(), table_.end(),
            [&](const value_type& lkv, const value_type& rkv) noexcept -> bool {
                return pot.get_parameter_comparator(lkv.second, rkv.second);
            });

        return PotentialT(max_iter->second).cutoff();
    }

    // accessors

    container_type const& table() const noexcept {return table_;}
    container_type&       table()       noexcept {return table_;}

    container_type&       parameters()       noexcept {return parameters_;}
    container_type const& parameters() const noexcept {return parameters_;}

    std::vector<std::size_t>&       participants()       noexcept {return participants_;}
    std::vector<std::size_t> const& participants() const noexcept {return participants_;}

  private:

    table_type               table_;
    std::vector<std::size_t> participants_;
    container_type           parameters_;
};

} // mjolnir
#endif// MJOLNIR_GLOBAL_PARAMETER_LIST_HPP
<commit_msg>feat:<commit_after>#ifndef MJOLNIR_GLOBAL_PARAMETER_LIST_HPP
#define MJOLNIR_GLOBAL_PARAMETER_LIST_HPP
#include <mjolnir/core/ExclusionList.hpp>
#include <mjolnir/core/System.hpp>
#include <vector>
#include <map>

namespace mjolnir
{

template<typename traitsT, typename potentialT>
struct ParameterCombinationRuleBase
{
  public:
    using traits_type         = traitsT;
    using potential_type      = potentialT;
    using pair_parameter_type = typename potential_type::parameter_type;

  public:

    virtual ~ParameterCombinationRuleBase() = default;

    // calculate combination of parameters
    virtual pair_parameter_type
    generate(const std::size_t i, const std::size_t j) const noexcept = 0;

    // This function is for cutoff distance
    virtual real_type max_cutoff_length(const potential_type& pot) const = 0;

    virtual std::vector<std::size_t>&       participants()       noexcept = 0;
    virtual std::vector<std::size_t> const& participants() const noexcept = 0;
};

//
// GlobalParameterList contains list of per-particle parameter used in a pair
// interaction and a combination rule.
//
template<typename traitsT, typename potentialT>
class GlobalParameterList
{
  public:
    using traits_type           = traitsT;
    using potential_type        = potentialT;
    using combination_rule_base = ParameterCombinationRuleBase<traits_type, potential_type>;
    using combination_rule_type = std::unique_ptr<combination_rule_base>;
    using pair_parameter_type   = typename potential_type::parameter_type;
    using system_type           = System<traits_type>;

    // topology stuff
    using topology_type        = Topology;
    using molecule_id_type     = typename topology_type::molecule_id_type;
    using group_id_type        = typename topology_type::group_id_type;
    using connection_kind_type = typename topology_type::connection_kind_type;
    using ignore_molecule_type = IgnoreMolecule<molecule_id_type>;
    using ignore_group_type    = IgnoreGroup   <group_id_type>;
    using exclusion_list_type  = ExclusionList <traits_type>;

  public:

    GlobalParameterList(combination_rule_type rule,
        const std::map<connection_kind_type, std::size_t>& exclusions,
        ignore_molecule_type ignore_mol, ignore_group_type ignore_grp)
      : rule_(std::move(rule)),
        exclusion_list_(exclusions, std::move(ignore_mol), std::move(ignore_grp))
    {}

    pair_parameter_type prepare_params(std::size_t i, std::size_t j) const noexcept
    {
        return rule_->generate(i, j);
    }

    // These value

    template<typename PotentialT>
    void initialize(const system_type& sys, const topology_type& topol,
                    const PotentialT& pot) noexcept
    {
        MJOLNIR_GET_DEFAULT_LOGGER();
        MJOLNIR_LOG_FUNCTION();

        this->update(sys, topol, pot);
        return;
    }

    template<typename PotentialT>
    void update(const system_type& sys, const topology_type& topol,
                const PotentialT& pot) noexcept
    {
        MJOLNIR_GET_DEFAULT_LOGGER();
        MJOLNIR_LOG_FUNCTION();

        this->max_cutoff_length_ = rule_->max_cutoff_length(pot);
        this->exclusion_list_.make(sys, topol);
        return;
    }

    real_type max_cutoff_length() const noexcept {return max_cutoff_length_;}

    // -----------------------------------------------------------------------
    // Here, the default implementation uses Newton's 3rd law to reduce
    // calculation. For an interacting pair (i, j), forces applied to i and j
    // are equal in magnitude and opposite in direction. So, if a pair (i, j) is
    // listed, (j, i) is not needed.
    //     See implementation of VerletList, CellList and GlobalPairInteraction
    // for more details about the usage of these functions.
    //
    bool has_interaction(const std::size_t i, const std::size_t j) const noexcept
    {
        // if not excluded, the pair has interaction.
        return (i < j) && !exclusion_list_.is_excluded(i, j);
    }
    std::vector<std::size_t> const& participants() const noexcept
    {
        return rule_->participants();
    }
    range<typename std::vector<std::size_t>::const_iterator>
    leading_participants() const noexcept
    {
        const auto& ps = rule_->participants();
        return make_range(ps.begin(), std::prev(ps.end()));
    }
    range<typename std::vector<std::size_t>::const_iterator>
    possible_partners_of(const std::size_t participant_idx,
                         const std::size_t /*particle_idx*/) const noexcept
    {
        return make_range(ps.begin() + participant_idx + 1, ps.end());
    }

    // ------------------------------------------------------------------------
    // the following accessers would be used in tests.

    exclusion_list_type const& exclusion_list() const noexcept
    {
        return exclusion_list_;
    }

    combination_rule_type&       rule()       noexcept {return rule_;}
    combination_rule_type const& rule() const noexcept {return rule_;}

  private:

    real_type                max_cutoff_length_;
    combination_rule_type    rule_;
    exclusion_list_type      exclusion_list_;
};

// ----------------------------------------------------------------------------
// widely-used parameter combination rules
//
// CombinationRules should provide the following two functionality.
// 1. combination rule
// 2. the pair parameter that provides the maximum cutoff distance
//
// The second one depends on the functional form of the potential, so the
// comparator used to find the max cutoff distance is given by the potential
// class via get_parameter_comparator().
//     To avoid O(n^2) search while finding the maximum cutoff distance, it
// linearly search the parameters using the comparator. It means that, the
// combination of the maximum parameters (two identical, maximum parameters)
// should give the maximum cutoff distance. This is automatically satisfied
// if you are using Lorentz-Berthelot rule or some other variant of it. If
// you are using combination table, the comparator directly compare the
// pair parameters. So, the comparator should be able to be used for both
// per-particle parameters and pair parameters.

template<typename traitsT, typename potentialT>
class LorentzBerthelotRule final
    : public ParameterCombinationRuleBase<traitsT, potentialT>
{
  public:
    using traits_type         = traitsT;
    using potential_type      = potentialT;
    using real_type           = typename traits_type::real_type;
    using parameter_type      = std::pair<real_type, real_type>;
    using pair_parameter_type = std::pair<real_type, real_type>;
    using container_type      = std::vector<parameter_type>;

  public:

    ~LorentzBerthelotRule() override {}

    explicit LorentzBerthelotRule(
        const std::vector<std::pair<std::size_t, parameter_type>>& parameters)
    {
        this->parameters_  .reserve(parameters.size());
        this->participants_.reserve(parameters.size());
        for(const auto& idxp : parameters)
        {
            const auto idx = idxp.first;
            this->participants_.push_back(idx);
            if(this->parameters_.size() <= idx)
            {
                this->parameters_.resize(idx+1, default_parameter);
            }
            this->parameters_.at(idx) = idxp.second;
        }
    }

    // calculate combination of parameters
    pair_parameter_type
    generate(const std::size_t i, const std::size_t j) const noexcept override
    {
        const auto& para1 = parameters_[i];
        const auto& para2 = parameters_[j];

        const auto sgm1 = para1.first;
        const auto eps1 = para1.second;
        const auto sgm2 = para2.first;
        const auto eps2 = para2.second;

        return std::make_pair((sgm1 + sgm2) / 2,
                             ((eps1 == eps2) ? eps1 : std::sqrt(eps1 * eps2)));
    }

    // This function is for cutoff distance
    real_type max_cutoff_length(const potential_type& pot) const override
    {
        if(parameters_.empty())
        {
            return std::numeric_limits<real_type>::infinity();
        }
        const auto max_iter = std::max_element(
                parameters_.begin(), parameters_.end(), pot.get_parameter_comparator());

        return PotentialT(*max_iter).cutoff();
    }

    // accessors for testing

    container_type&       parameters()       noexcept {return parameters_;}
    container_type const& parameters() const noexcept {return parameters_;}

    std::vector<std::size_t>&       participants()       noexcept override {return participants_;}
    std::vector<std::size_t> const& participants() const noexcept override {return participants_;}

  private:

    std::vector<std::size_t> participants_;
    container_type           parameters_;
};

template<typename traitsT, typename potentialT>
struct CombinationTable
    : public ParameterCombinationRuleBase<traitsT, potentialT>
{
  public:
    using traits_type         = traitsT;
    using potential_type      = potentialT;
    using parameter_type      = std::string;
    using pair_parameter_type = typename potential_type::parameter_type;
    using container_type      = std::vector<parameter_type>;
    using table_type          = std::unordered_map<std::string, pair_parameter_type>;

  public:

    ~CombinationTable() override {}

    CombinationTable(
        const std::vector<std::pair<std::size_t, parameter_type>>& parameters,
        table_type&& table)
        : table_(std::move(table))
    {
        this->parameters_  .reserve(parameters.size());
        this->participants_.reserve(parameters.size());
        for(const auto& idxp : parameters)
        {
            const auto idx = idxp.first;
            this->participants_.push_back(idx);
            if(this->parameters_.size() <= idx)
            {
                this->parameters_.resize(idx+1, default_parameter);
            }
            this->parameters_.at(idx) = idxp.second;
        }
    }

    // Since it concats the names, it is better to keep parameter name short.
    pair_parameter_type
    generate(const std::size_t i, const std::size_t j) const noexcept override
    {
        const auto& para1 = parameters_[i];
        const auto& para2 = parameters_[j];
        return table_[para1 + para2];
    }

    // This function is for cutoff distance
    real_type max_cutoff_length(const potential_type& pot) const override
    {
        if(parameters_.empty())
        {
            return std::numeric_limits<real_type>::infinity();
        }

        using value_type = typename table_type::value_type; // kv-pair
        const auto max_iter = std::max_element(table_.begin(), table_.end(),
            [&](const value_type& lkv, const value_type& rkv) noexcept -> bool {
                return pot.get_parameter_comparator(lkv.second, rkv.second);
            });

        return PotentialT(max_iter->second).cutoff();
    }

    // accessors

    table_type const& table() const noexcept {return table_;}
    table_type&       table()       noexcept {return table_;}

    container_type&       parameters()       noexcept {return parameters_;}
    container_type const& parameters() const noexcept {return parameters_;}

    std::vector<std::size_t>&       participants()       noexcept override {return participants_;}
    std::vector<std::size_t> const& participants() const noexcept override {return participants_;}

  private:

    table_type               table_;
    std::vector<std::size_t> participants_;
    container_type           parameters_;
};

} // mjolnir
#endif// MJOLNIR_GLOBAL_PARAMETER_LIST_HPP
<|endoftext|>


<commit_before>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * 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/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */

#include "drawingml/chart/titlecontext.hxx"

#include "drawingml/shapepropertiescontext.hxx"
#include "drawingml/textbodycontext.hxx"
#include "drawingml/chart/datasourcecontext.hxx"
#include "drawingml/chart/titlemodel.hxx"

#include "rtl/ustrbuf.hxx"
#include <osl/diagnose.h>

namespace oox {
namespace drawingml {
namespace chart {

using ::oox::core::ContextHandler2Helper;
using ::oox::core::ContextHandlerRef;

TextContext::TextContext( ContextHandler2Helper& rParent, TextModel& rModel ) :
    ContextBase< TextModel >( rParent, rModel )
{
}

TextContext::~TextContext()
{
}

ContextHandlerRef TextContext::onCreateContext( sal_Int32 nElement, const AttributeList& )
{
    // this context handler is used for <c:tx> and embedded <c:v> elements
    if( isCurrentElement( C_TOKEN( tx ) ) ) switch( nElement )
    {
        case C_TOKEN( rich ):
            return new TextBodyContext( *this, mrModel.mxTextBody.create() );

        case C_TOKEN( strRef ):
            OSL_ENSURE( !mrModel.mxDataSeq, "TextContext::onCreateContext - multiple data sequences" );
            return new StringSequenceContext( *this, mrModel.mxDataSeq.create() );

        case C_TOKEN( v ):
            OSL_ENSURE( !mrModel.mxDataSeq, "TextContext::onCreateContext - multiple data sequences" );
            return this;    // collect value in onCharacters()
    }
    return 0;
}

void TextContext::onCharacters( const OUString& rChars )
{
    if( isCurrentElement( C_TOKEN( v ) ) )
    {
        // Static text is stored as a single string formula token for Excel document.
        OUStringBuffer aBuf;
        aBuf.append('"').append(rChars).append('"');
        mrModel.mxDataSeq.create().maFormula = aBuf.makeStringAndClear();

        // Also store it as a single element type for non-Excel document.
        mrModel.mxDataSeq->maData[0] <<= rChars;
    }
}

TitleContext::TitleContext( ContextHandler2Helper& rParent, TitleModel& rModel ) :
    ContextBase< TitleModel >( rParent, rModel )
{
}

TitleContext::~TitleContext()
{
}

ContextHandlerRef TitleContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
{
    bool bMSO2007Doc = getFilter().isMSO2007Document();
    // this context handler is used for <c:title> only
    switch( nElement )
    {
        case C_TOKEN( layout ):
            return new LayoutContext( *this, mrModel.mxLayout.create() );

        case C_TOKEN( overlay ):
            mrModel.mbOverlay = rAttribs.getBool( XML_val, !bMSO2007Doc );
            return 0;

        case C_TOKEN( spPr ):
            return new ShapePropertiesContext( *this, mrModel.mxShapeProp.create() );

        case C_TOKEN( tx ):
            return new TextContext( *this, mrModel.mxText.create() );

        case C_TOKEN( txPr ):
            return new TextBodyContext( *this, mrModel.mxTextProp.create() );
    }
    return 0;
}

LegendContext::LegendContext( ContextHandler2Helper& rParent, LegendModel& rModel ) :
    ContextBase< LegendModel >( rParent, rModel )
{
}

LegendContext::~LegendContext()
{
}

ContextHandlerRef LegendContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
{
    bool bMSO2007Doc = getFilter().isMSO2007Document();
    // this context handler is used for <c:legend> only
    switch( nElement )
    {
        case C_TOKEN( layout ):
            return new LayoutContext( *this, mrModel.mxLayout.create() );

        case C_TOKEN( legendPos ):
            mrModel.mnPosition = rAttribs.getToken( XML_val, XML_r );
            return 0;

        case C_TOKEN( overlay ):
            // default is 'false', not 'true' as specified
            mrModel.mbOverlay = rAttribs.getBool( XML_val, !bMSO2007Doc );
            return 0;

        case C_TOKEN( spPr ):
            return new ShapePropertiesContext( *this, mrModel.mxShapeProp.create() );

        case C_TOKEN( txPr ):
            return new TextBodyContext( *this, mrModel.mxTextProp.create() );
    }
    return 0;
}

} // namespace chart
} // namespace drawingml
} // namespace oox

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
<commit_msg>remove old misleading comment<commit_after>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * 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/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */

#include "drawingml/chart/titlecontext.hxx"

#include "drawingml/shapepropertiescontext.hxx"
#include "drawingml/textbodycontext.hxx"
#include "drawingml/chart/datasourcecontext.hxx"
#include "drawingml/chart/titlemodel.hxx"

#include "rtl/ustrbuf.hxx"
#include <osl/diagnose.h>

namespace oox {
namespace drawingml {
namespace chart {

using ::oox::core::ContextHandler2Helper;
using ::oox::core::ContextHandlerRef;

TextContext::TextContext( ContextHandler2Helper& rParent, TextModel& rModel ) :
    ContextBase< TextModel >( rParent, rModel )
{
}

TextContext::~TextContext()
{
}

ContextHandlerRef TextContext::onCreateContext( sal_Int32 nElement, const AttributeList& )
{
    // this context handler is used for <c:tx> and embedded <c:v> elements
    if( isCurrentElement( C_TOKEN( tx ) ) ) switch( nElement )
    {
        case C_TOKEN( rich ):
            return new TextBodyContext( *this, mrModel.mxTextBody.create() );

        case C_TOKEN( strRef ):
            OSL_ENSURE( !mrModel.mxDataSeq, "TextContext::onCreateContext - multiple data sequences" );
            return new StringSequenceContext( *this, mrModel.mxDataSeq.create() );

        case C_TOKEN( v ):
            OSL_ENSURE( !mrModel.mxDataSeq, "TextContext::onCreateContext - multiple data sequences" );
            return this;    // collect value in onCharacters()
    }
    return 0;
}

void TextContext::onCharacters( const OUString& rChars )
{
    if( isCurrentElement( C_TOKEN( v ) ) )
    {
        // Static text is stored as a single string formula token for Excel document.
        OUStringBuffer aBuf;
        aBuf.append('"').append(rChars).append('"');
        mrModel.mxDataSeq.create().maFormula = aBuf.makeStringAndClear();

        // Also store it as a single element type for non-Excel document.
        mrModel.mxDataSeq->maData[0] <<= rChars;
    }
}

TitleContext::TitleContext( ContextHandler2Helper& rParent, TitleModel& rModel ) :
    ContextBase< TitleModel >( rParent, rModel )
{
}

TitleContext::~TitleContext()
{
}

ContextHandlerRef TitleContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
{
    bool bMSO2007Doc = getFilter().isMSO2007Document();
    // this context handler is used for <c:title> only
    switch( nElement )
    {
        case C_TOKEN( layout ):
            return new LayoutContext( *this, mrModel.mxLayout.create() );

        case C_TOKEN( overlay ):
            mrModel.mbOverlay = rAttribs.getBool( XML_val, !bMSO2007Doc );
            return 0;

        case C_TOKEN( spPr ):
            return new ShapePropertiesContext( *this, mrModel.mxShapeProp.create() );

        case C_TOKEN( tx ):
            return new TextContext( *this, mrModel.mxText.create() );

        case C_TOKEN( txPr ):
            return new TextBodyContext( *this, mrModel.mxTextProp.create() );
    }
    return 0;
}

LegendContext::LegendContext( ContextHandler2Helper& rParent, LegendModel& rModel ) :
    ContextBase< LegendModel >( rParent, rModel )
{
}

LegendContext::~LegendContext()
{
}

ContextHandlerRef LegendContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
{
    bool bMSO2007Doc = getFilter().isMSO2007Document();
    // this context handler is used for <c:legend> only
    switch( nElement )
    {
        case C_TOKEN( layout ):
            return new LayoutContext( *this, mrModel.mxLayout.create() );

        case C_TOKEN( legendPos ):
            mrModel.mnPosition = rAttribs.getToken( XML_val, XML_r );
            return 0;

        case C_TOKEN( overlay ):
            mrModel.mbOverlay = rAttribs.getBool( XML_val, !bMSO2007Doc );
            return 0;

        case C_TOKEN( spPr ):
            return new ShapePropertiesContext( *this, mrModel.mxShapeProp.create() );

        case C_TOKEN( txPr ):
            return new TextBodyContext( *this, mrModel.mxTextProp.create() );
    }
    return 0;
}

} // namespace chart
} // namespace drawingml
} // namespace oox

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
<|endoftext|>


<commit_before>/**
  * Touhou Community Reliant Automatic Patcher
  * Tasogare Frontier support plugin
  *
  * ----
  *
  * Plugin entry point and breakpoints.
  */

#include <thcrap.h>
#include <vfs.h>
#include "thcrap_tasofro.h"
#include "pl.h"
#include "tfcs.h"
#include "act-nut.h"
#include "spellcards_generator.h"
#include "plugin.h"

// TODO: read the file names list in JSON format
int __stdcall thcrap_plugin_init()
{
	char* filenames_list;
	size_t filenames_list_size;

	int base_tasofro_removed = stack_remove_if_unneeded("base_tasofro");
	if (base_tasofro_removed == 1) {
		return 1;
	} else if(base_tasofro_removed == -1) {
		const char *game = json_object_get_string(runconfig_get(), "game");
		if(game && !strcmp(game, "th145")) {
			log_mboxf(NULL, MB_OK | MB_ICONINFORMATION,
				"Support for TH14.5 has been moved out of the sandbox.\n"
				"\n"
				"Please reconfigure your patch stack; otherwise, you might "
				"encounter errors or missing functionality after further "
				"updates.\n"
			);
		}
	}

	patchhook_register("*/stage*.pl", patch_pl);
	patchhook_register("*/ed_*.pl", patch_pl);
	patchhook_register("*.csv", patch_tfcs);
	patchhook_register("*.dll", patch_dll);
	patchhook_register("*.act", patch_act);
	patchhook_register("*.nut", patch_nut);

	jsonvfs_game_add("data/csv/story/*/stage*.csv.jdiff",						{ "spells.js" }, spell_story_generator);
	jsonvfs_game_add("data/csv/spellcard/*.csv.jdiff",							{ "spells.js" }, spell_player_generator);
	jsonvfs_game_add("data/system/char_select3/*/equip/*/000.png.csv.jdiff",	{ "spells.js" }, spell_char_select_generator);

	filenames_list = (char*)stack_game_file_resolve("fileslist.txt", &filenames_list_size);
	LoadFileNameListFromMemory(filenames_list, filenames_list_size);
	return 0;
}

int BP_file_header(x86_reg_t *regs, json_t *bp_info)
{
	// Parameters
	// ----------
	BYTE **pHeader = (BYTE**)json_object_get_register(bp_info, regs, "struct");
	DWORD **key = (DWORD**)json_object_get_register(bp_info, regs, "key");
	// ----------
	if (!pHeader || !*pHeader || !key || !*key)
		return 1;

	struct FileHeader *header = (struct FileHeader*)(*pHeader + json_integer_value(json_object_get(bp_info, "struct_offset")));
	struct FileHeaderFull *full_header = register_file_header(header, *key);
	file_rep_t *fr = &full_header->fr;

	if (full_header->path[0]) {
		file_rep_init(fr, full_header->path);
	}

	// If the game loads a DDS file and we have no corresponding DDS file, try to replace it with a PNG file (the game will deal with it)
	if (fr->rep_buffer == NULL && strlen(fr->name) > 4 && strcmp(fr->name + strlen(fr->name) - 4, ".dds") == 0) {
		char png_path[MAX_PATH];
		strcpy(png_path, full_header->path);
		strcpy(png_path + strlen(full_header->path) - 3, "png");
		file_rep_clear(fr);
		file_rep_init(fr, png_path);
	}

	if (fr->rep_buffer != NULL) {
		full_header->size = fr->pre_json_size + fr->patch_size;
		header->size = full_header->size;

		fr->game_buffer = malloc(full_header->size);
		memcpy(fr->game_buffer, fr->rep_buffer, fr->pre_json_size);
		patchhooks_run( // TODO: replace with file_rep_hooks_run
			fr->hooks, fr->game_buffer, fr->pre_json_size + fr->patch_size, fr->pre_json_size, fr->patch
			);

		crypt_block((BYTE*)fr->game_buffer, full_header->size, full_header->key);
	}

	if (fr->rep_buffer == NULL && fr->patch != NULL) {
		// If we have no rep buffer but we have a patch buffer, we will patch the file later, so we need to keep the file_rep.
		header->size += fr->patch_size;
		full_header->size = header->size;
	}
	else {
		file_rep_clear(fr);
	}

	return 1;
}

/**
  * Replace file, 3rd attempt - hopefully I won't have to write a 4th one.
  * This breakpoint takes care of patching the files.
  * It takes place in the game's file reader, right after it calls its ReadFile caller.
  * At this point, we have the game's file structure in ESI, and so the filename's hash - we can't
  * read the wrong file.
  * The game's file structure have the following fields:
  * DWORD unknown1;
  * HANDLE hFile;
  * BYTE buffer[0x10000];
  * DWORD LastReadFileSize; // Last value returned in lpNumberOfBytesRead in ReadFile
  * DWORD Offset; // Offset for the reader. I don't know what happens to it when it's bigger than 0x10000
  * DWORD LastReadSize; // Last read size asked to the reader
  * DWORD unknown2;
  * DWORD FileSize;
  * DWORD FileNameHash;
  * DWORD unknown3;
  * DWORD XorOffset; // Offset used by the XOR function
  * DWORD Key[5]; // 32-bytes encryption key used for XORing. The 5th DWORD is a copy of the 1st DWORD.
  * DWORD Aux; // Contains the last DWORD read from the file. Used during XORing.
  * We use hFile (file_struct + 4), buffer (file_struct + 8) and FileNameHash (file_struct + 0x1001c).
  * All the other fields are listed for documentation.
  */
int BP_replace_file(x86_reg_t *regs, json_t *bp_info)
{
	// Parameters
	// ----------
	json_t *jBuffer = json_object_get(bp_info, "buffer");
	json_t *jSize = json_object_get(bp_info, "size");
	BYTE **file_struct = (BYTE**)json_object_get_register(bp_info, regs, "file_struct");
	BYTE **pBuffer = (BYTE**)reg(regs, json_string_value(jBuffer));
	DWORD *pSize = (DWORD*)reg(regs, json_string_value(jSize));
	// ----------

	static DWORD size = 0;
	static BYTE *buffer = NULL;

	if (pBuffer && *pBuffer) {
		buffer = *pBuffer;
	}
	else if (jBuffer) {
		buffer = (BYTE*)json_integer_value(jBuffer);
	}
	if (pSize) {
		size = *pSize;
	}
	else if (jSize) {
		size = (DWORD)json_integer_value(jSize);
	}

	if (!file_struct) {
		return 1;
	}

	struct FileHeaderFull *header = hash_to_file_header(*(DWORD*)(*file_struct + 0x1001c));
	if (!header || !header->path[0] || (!header->fr.game_buffer && !header->fr.patch)) {
		// Nothing to patch.
		return 1;
	}

	HANDLE hFile = *(HANDLE*)(*file_struct + 4);
	if (buffer == NULL) {
		buffer = *file_struct + 8;
	}
	if (size == 0) {
		size = 65536;
	}

	if (header->effective_offset == -1) {
		header->effective_offset = SetFilePointer(hFile, 0, NULL, FILE_CURRENT) - size;

		// We couldn't patch the file earlier, but now we have a hFile and an offset, so we should be able to.
		if (header->fr.game_buffer == NULL && header->fr.patch != NULL) {
			SetFilePointer(hFile, -(int)size, NULL, FILE_CURRENT);

			DWORD nbOfBytesRead;
			header->fr.game_buffer = malloc(header->size);
			ReadFile(hFile, header->fr.game_buffer, header->orig_size, &nbOfBytesRead, NULL);

			uncrypt_block((BYTE*)header->fr.game_buffer, header->orig_size, header->key);
			patchhooks_run(
				header->fr.hooks, header->fr.game_buffer, header->size, header->orig_size, header->fr.patch
				);
			crypt_block((BYTE*)header->fr.game_buffer, header->size, header->key);

			SetFilePointer(hFile, header->effective_offset + size, NULL, FILE_BEGIN);
			file_rep_clear(&header->fr);
		}
	}

	size_t offset = SetFilePointer(hFile, 0, NULL, FILE_CURRENT) - size - header->effective_offset;
	int copy_size = min(header->size - offset, size);

	log_printf("[replace_file]  known path: %s, hash %.8x, offset: %d, requested size %d, file_rep_size left: %d, chosen size: %d\n",
		header->path, *(DWORD*)(*file_struct + 0x1001c), offset, size, header->size - offset, copy_size);
	memcpy(buffer, (BYTE*)header->fr.game_buffer + offset, copy_size);

	buffer = NULL;
	size = 0;
	return 1;
}
<commit_msg>Tasofro: Actually unload thcrap_tasofro.dll for empty patch stacks.<commit_after>/**
  * Touhou Community Reliant Automatic Patcher
  * Tasogare Frontier support plugin
  *
  * ----
  *
  * Plugin entry point and breakpoints.
  */

#include <thcrap.h>
#include <vfs.h>
#include "thcrap_tasofro.h"
#include "pl.h"
#include "tfcs.h"
#include "act-nut.h"
#include "spellcards_generator.h"
#include "plugin.h"

// TODO: read the file names list in JSON format
int __stdcall thcrap_plugin_init()
{
	char* filenames_list;
	size_t filenames_list_size;

	int base_tasofro_removed = stack_remove_if_unneeded("base_tasofro");
	if (base_tasofro_removed == 1) {
		return 1;
	} else if(base_tasofro_removed == -1) {
		const char *game = json_object_get_string(runconfig_get(), "game");
		if(game && !strcmp(game, "th145")) {
			log_mboxf(NULL, MB_OK | MB_ICONINFORMATION,
				"Support for TH14.5 has been moved out of the sandbox.\n"
				"\n"
				"Please reconfigure your patch stack; otherwise, you might "
				"encounter errors or missing functionality after further "
				"updates.\n"
			);
		} else {
			return 1;
		}
	}

	patchhook_register("*/stage*.pl", patch_pl);
	patchhook_register("*/ed_*.pl", patch_pl);
	patchhook_register("*.csv", patch_tfcs);
	patchhook_register("*.dll", patch_dll);
	patchhook_register("*.act", patch_act);
	patchhook_register("*.nut", patch_nut);

	jsonvfs_game_add("data/csv/story/*/stage*.csv.jdiff",						{ "spells.js" }, spell_story_generator);
	jsonvfs_game_add("data/csv/spellcard/*.csv.jdiff",							{ "spells.js" }, spell_player_generator);
	jsonvfs_game_add("data/system/char_select3/*/equip/*/000.png.csv.jdiff",	{ "spells.js" }, spell_char_select_generator);

	filenames_list = (char*)stack_game_file_resolve("fileslist.txt", &filenames_list_size);
	LoadFileNameListFromMemory(filenames_list, filenames_list_size);
	return 0;
}

int BP_file_header(x86_reg_t *regs, json_t *bp_info)
{
	// Parameters
	// ----------
	BYTE **pHeader = (BYTE**)json_object_get_register(bp_info, regs, "struct");
	DWORD **key = (DWORD**)json_object_get_register(bp_info, regs, "key");
	// ----------
	if (!pHeader || !*pHeader || !key || !*key)
		return 1;

	struct FileHeader *header = (struct FileHeader*)(*pHeader + json_integer_value(json_object_get(bp_info, "struct_offset")));
	struct FileHeaderFull *full_header = register_file_header(header, *key);
	file_rep_t *fr = &full_header->fr;

	if (full_header->path[0]) {
		file_rep_init(fr, full_header->path);
	}

	// If the game loads a DDS file and we have no corresponding DDS file, try to replace it with a PNG file (the game will deal with it)
	if (fr->rep_buffer == NULL && strlen(fr->name) > 4 && strcmp(fr->name + strlen(fr->name) - 4, ".dds") == 0) {
		char png_path[MAX_PATH];
		strcpy(png_path, full_header->path);
		strcpy(png_path + strlen(full_header->path) - 3, "png");
		file_rep_clear(fr);
		file_rep_init(fr, png_path);
	}

	if (fr->rep_buffer != NULL) {
		full_header->size = fr->pre_json_size + fr->patch_size;
		header->size = full_header->size;

		fr->game_buffer = malloc(full_header->size);
		memcpy(fr->game_buffer, fr->rep_buffer, fr->pre_json_size);
		patchhooks_run( // TODO: replace with file_rep_hooks_run
			fr->hooks, fr->game_buffer, fr->pre_json_size + fr->patch_size, fr->pre_json_size, fr->patch
			);

		crypt_block((BYTE*)fr->game_buffer, full_header->size, full_header->key);
	}

	if (fr->rep_buffer == NULL && fr->patch != NULL) {
		// If we have no rep buffer but we have a patch buffer, we will patch the file later, so we need to keep the file_rep.
		header->size += fr->patch_size;
		full_header->size = header->size;
	}
	else {
		file_rep_clear(fr);
	}

	return 1;
}

/**
  * Replace file, 3rd attempt - hopefully I won't have to write a 4th one.
  * This breakpoint takes care of patching the files.
  * It takes place in the game's file reader, right after it calls its ReadFile caller.
  * At this point, we have the game's file structure in ESI, and so the filename's hash - we can't
  * read the wrong file.
  * The game's file structure have the following fields:
  * DWORD unknown1;
  * HANDLE hFile;
  * BYTE buffer[0x10000];
  * DWORD LastReadFileSize; // Last value returned in lpNumberOfBytesRead in ReadFile
  * DWORD Offset; // Offset for the reader. I don't know what happens to it when it's bigger than 0x10000
  * DWORD LastReadSize; // Last read size asked to the reader
  * DWORD unknown2;
  * DWORD FileSize;
  * DWORD FileNameHash;
  * DWORD unknown3;
  * DWORD XorOffset; // Offset used by the XOR function
  * DWORD Key[5]; // 32-bytes encryption key used for XORing. The 5th DWORD is a copy of the 1st DWORD.
  * DWORD Aux; // Contains the last DWORD read from the file. Used during XORing.
  * We use hFile (file_struct + 4), buffer (file_struct + 8) and FileNameHash (file_struct + 0x1001c).
  * All the other fields are listed for documentation.
  */
int BP_replace_file(x86_reg_t *regs, json_t *bp_info)
{
	// Parameters
	// ----------
	json_t *jBuffer = json_object_get(bp_info, "buffer");
	json_t *jSize = json_object_get(bp_info, "size");
	BYTE **file_struct = (BYTE**)json_object_get_register(bp_info, regs, "file_struct");
	BYTE **pBuffer = (BYTE**)reg(regs, json_string_value(jBuffer));
	DWORD *pSize = (DWORD*)reg(regs, json_string_value(jSize));
	// ----------

	static DWORD size = 0;
	static BYTE *buffer = NULL;

	if (pBuffer && *pBuffer) {
		buffer = *pBuffer;
	}
	else if (jBuffer) {
		buffer = (BYTE*)json_integer_value(jBuffer);
	}
	if (pSize) {
		size = *pSize;
	}
	else if (jSize) {
		size = (DWORD)json_integer_value(jSize);
	}

	if (!file_struct) {
		return 1;
	}

	struct FileHeaderFull *header = hash_to_file_header(*(DWORD*)(*file_struct + 0x1001c));
	if (!header || !header->path[0] || (!header->fr.game_buffer && !header->fr.patch)) {
		// Nothing to patch.
		return 1;
	}

	HANDLE hFile = *(HANDLE*)(*file_struct + 4);
	if (buffer == NULL) {
		buffer = *file_struct + 8;
	}
	if (size == 0) {
		size = 65536;
	}

	if (header->effective_offset == -1) {
		header->effective_offset = SetFilePointer(hFile, 0, NULL, FILE_CURRENT) - size;

		// We couldn't patch the file earlier, but now we have a hFile and an offset, so we should be able to.
		if (header->fr.game_buffer == NULL && header->fr.patch != NULL) {
			SetFilePointer(hFile, -(int)size, NULL, FILE_CURRENT);

			DWORD nbOfBytesRead;
			header->fr.game_buffer = malloc(header->size);
			ReadFile(hFile, header->fr.game_buffer, header->orig_size, &nbOfBytesRead, NULL);

			uncrypt_block((BYTE*)header->fr.game_buffer, header->orig_size, header->key);
			patchhooks_run(
				header->fr.hooks, header->fr.game_buffer, header->size, header->orig_size, header->fr.patch
				);
			crypt_block((BYTE*)header->fr.game_buffer, header->size, header->key);

			SetFilePointer(hFile, header->effective_offset + size, NULL, FILE_BEGIN);
			file_rep_clear(&header->fr);
		}
	}

	size_t offset = SetFilePointer(hFile, 0, NULL, FILE_CURRENT) - size - header->effective_offset;
	int copy_size = min(header->size - offset, size);

	log_printf("[replace_file]  known path: %s, hash %.8x, offset: %d, requested size %d, file_rep_size left: %d, chosen size: %d\n",
		header->path, *(DWORD*)(*file_struct + 0x1001c), offset, size, header->size - offset, copy_size);
	memcpy(buffer, (BYTE*)header->fr.game_buffer + offset, copy_size);

	buffer = NULL;
	size = 0;
	return 1;
}
<|endoftext|>