<commit_before>/*
 *  This file is part of Poedit (http://poedit.net)
 *
 *  Copyright (C) 1999-2015 Vaclav Slavik
 *  Copyright (C) 2005 Olivier Sannier
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a
 *  copy of this software and associated documentation files (the "Software"),
 *  to deal in the Software without restriction, including without limitation
 *  the rights to use, copy, modify, merge, publish, distribute, sublicense,
 *  and/or sell copies of the Software, and to permit persons to whom the
 *  Software is furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in
 *  all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 *  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 *  DEALINGS IN THE SOFTWARE.
 *
 */

#include "edlistctrl.h"

#include "hidpi.h"
#include "language.h"
#include "digits.h"
#include "cat_sorting.h"

#include <wx/wx.h>
#include <wx/imaglist.h>
#include <wx/artprov.h>
#include <wx/dcmemory.h>
#include <wx/image.h>
#include <wx/wupdlock.h>

#include <algorithm>

namespace
{

// how much to darken the other color in shaded list (this value
// is what GTK+ uses in its tree view control)
#define DARKEN_FACTOR      0.95

// max difference in color to consider it "amost" same if it differs by most
// this amount (in 0..255 range) from the tested color:
#define COLOR_SIMILARITY_FACTOR  20

inline bool IsAlmostBlack(const wxColour& clr)
{
    return (clr.Red() <= COLOR_SIMILARITY_FACTOR &&
            clr.Green() <= COLOR_SIMILARITY_FACTOR &&
            clr.Blue() <= COLOR_SIMILARITY_FACTOR);
}

inline bool IsAlmostWhite(const wxColour& clr)
{
    return (clr.Red() >= 255-COLOR_SIMILARITY_FACTOR &&
            clr.Green() >= 255-COLOR_SIMILARITY_FACTOR &&
            clr.Blue() >= 255-COLOR_SIMILARITY_FACTOR);
}

// colours used in the list:

const wxColour gs_ErrorColor("#ff5050");

// colors for white list control background
const wxColour gs_UntranslatedForWhite("#103f67");
const wxColour gs_FuzzyForWhite("#a9861b");

// ditto for black background
const wxColour gs_UntranslatedForBlack("#1962a0");
const wxColour gs_FuzzyForBlack("#a9861b");

const wxColour gs_TranspColor(254, 0, 253); // FIXME: get rid of this

enum
{
    IMG_NOTHING,
    IMG_AUTOMATIC,
    IMG_COMMENT,
    IMG_BOOKMARK
};

} // anonymous namespace

BEGIN_EVENT_TABLE(PoeditListCtrl, wxListCtrl)
   EVT_SIZE(PoeditListCtrl::OnSize)
END_EVENT_TABLE()

PoeditListCtrl::PoeditListCtrl(wxWindow *parent,
               wxWindowID id,
               const wxPoint &pos,
               const wxSize &size,
               long style,
               bool dispIDs,
               const wxValidator& validator,
               const wxString &name)
     : wxListView(parent, id, pos, size, style | wxLC_VIRTUAL, validator, name)
{
    m_catalog = NULL;
    m_displayIDs = dispIDs;

    m_isRTL = false;
    m_appIsRTL = (wxTheApp->GetLayoutDirection() == wxLayout_RightToLeft);

    sortOrder = SortOrder::Default();

    CreateColumns();

    wxImageList *list = new wxImageList(PX(16), PX(16));

    // IMG_XXX:
    list->Add(wxArtProvider::GetBitmap("poedit-status-nothing"));
    list->Add(wxArtProvider::GetBitmap("poedit-status-automatic"));
    list->Add(wxArtProvider::GetBitmap("poedit-status-comment"));
    list->Add(wxArtProvider::GetBitmap("poedit-status-bookmark"));

    AssignImageList(list, wxIMAGE_LIST_SMALL);

    // configure items colors & fonts:

    wxVisualAttributes visual = GetDefaultAttributes();
    wxColour shaded = visual.colBg;

#ifdef __WXMSW__
    // On Windows 7, shaded list items make it impossible to see the selection,
    // so use different color for it (see bug #336).
    int verMaj, verMin;
    wxGetOsVersion(&verMaj, &verMin);
    if ( verMaj > 6 || (verMaj == 6 && verMin >= 1) )
    {
        shaded.Set(int(0.99 * shaded.Red()),
                   int(0.99 * shaded.Green()),
                   shaded.Blue());
    }
    else
#endif // __WXMSW__
#ifdef __WXOSX__
    if ( shaded == *wxWHITE )
    {
        // use standard shaded color from finder/databrowser:
        shaded.Set("#f0f5fd");
    }
    else
#endif // __WXOSX__
    {
        shaded.Set(int(DARKEN_FACTOR * shaded.Red()),
                   int(DARKEN_FACTOR * shaded.Green()),
                   int(DARKEN_FACTOR * shaded.Blue()));
    }

    m_attrNormal[1].SetBackgroundColour(shaded);
    m_attrUntranslated[1].SetBackgroundColour(shaded);
    m_attrFuzzy[1].SetBackgroundColour(shaded);
    m_attrInvalid[1].SetBackgroundColour(shaded);

    // FIXME: make this user-configurable
    if ( IsAlmostWhite(visual.colBg) )
    {
        m_attrUntranslated[0].SetTextColour(gs_UntranslatedForWhite);
        m_attrUntranslated[1].SetTextColour(gs_UntranslatedForWhite);
        m_attrFuzzy[0].SetTextColour(gs_FuzzyForWhite);
        m_attrFuzzy[1].SetTextColour(gs_FuzzyForWhite);
    }
    else if ( IsAlmostBlack(visual.colBg) )
    {
        m_attrUntranslated[0].SetTextColour(gs_UntranslatedForBlack);
        m_attrUntranslated[1].SetTextColour(gs_UntranslatedForBlack);
        m_attrFuzzy[0].SetTextColour(gs_FuzzyForBlack);
        m_attrFuzzy[1].SetTextColour(gs_FuzzyForBlack);
    }
    // else: we don't know if the default colors would be well-visible on
    //       user's background color, so play it safe and don't highlight
    //       anything

    // FIXME: todo; use appropriate font for fuzzy/trans/untrans
    m_attrInvalid[0].SetBackgroundColour(gs_ErrorColor);
    m_attrInvalid[1].SetBackgroundColour(gs_ErrorColor);

    // Use gray for IDs
    if ( IsAlmostBlack(visual.colFg) )
        m_attrId.SetTextColour(wxColour("#A1A1A1"));

    SetCustomFont(wxNullFont);
}

PoeditListCtrl::~PoeditListCtrl()
{
    sortOrder.Save();
}

void PoeditListCtrl::SetCustomFont(wxFont font_)
{
    wxFont font(font_);

    if ( !font.IsOk() )
        font = GetDefaultAttributes().font;

    m_attrNormal[0].SetFont(font);
    m_attrNormal[1].SetFont(font);

    wxFont fontb = font;
    fontb.SetWeight(wxFONTWEIGHT_BOLD);

    m_attrUntranslated[0].SetFont(fontb);
    m_attrUntranslated[1].SetFont(fontb);

    m_attrFuzzy[0].SetFont(fontb);
    m_attrFuzzy[1].SetFont(fontb);

    m_attrInvalid[0].SetFont(fontb);
    m_attrInvalid[1].SetFont(fontb);
}

void PoeditListCtrl::SetDisplayLines(bool dl)
{
    m_displayIDs = dl;
    CreateColumns();
}

void PoeditListCtrl::CreateColumns()
{
    DeleteAllColumns();
    InsertColumn(0, _("Source text"));
    InsertColumn(1, _("Translation"));
    if (m_displayIDs)
        InsertColumn(2, _("ID"), wxLIST_FORMAT_RIGHT);

#ifdef __WXMSW__
    if (m_appIsRTL)
    {
        // another wx quirk: if we truly need left alignment, we must lie under RTL locales
        wxListItem colInfoOrig;
        colInfoOrig.SetAlign(wxLIST_FORMAT_RIGHT);
        SetColumn(0, colInfoOrig);
    }
#endif

    SizeColumns();
}

void PoeditListCtrl::SizeColumns()
{
    const int LINE_COL_SIZE = m_displayIDs ? PX(50) : 0;

    int w = GetSize().x
            - wxSystemSettings::GetMetric(wxSYS_VSCROLL_X) - 10
            - LINE_COL_SIZE;
    SetColumnWidth(0, w / 2);
    SetColumnWidth(1, w - w / 2);
    if (m_displayIDs)
        SetColumnWidth(2, LINE_COL_SIZE);

    m_colWidth = (w/2) / GetCharWidth();
}

void PoeditListCtrl::CatalogChanged(Catalog* catalog)
{
    wxWindowUpdateLocker no_updates(this);

    const bool isSameCatalog = (catalog == m_catalog);

    std::vector<int> selection;
    if (isSameCatalog)
        selection = GetSelectedCatalogItems();

    // this is to prevent crashes (wxMac at least) when shortening virtual
    // listctrl when its scrolled to the bottom:
    m_catalog = nullptr;
    SetItemCount(0);

    // now read the new catalog:
    m_catalog = catalog;
    ReadCatalog();

    if (isSameCatalog && !selection.empty())
        SetSelectedCatalogItems(selection);
}

void PoeditListCtrl::ReadCatalog()
{
    wxWindowUpdateLocker no_updates(this);

    // clear the list and its sort order too:
    SetItemCount(0);
    m_mapListToCatalog.clear();
    m_mapCatalogToList.clear();

    if (m_catalog == NULL)
    {
        Refresh();
        return;
    }

    auto lang = m_catalog->GetLanguage();
    auto isRTL = lang.IsRTL();
#ifdef __WXMSW__
    // a quirk of wx API: if the current locale is RTL, the meaning of L and R is reversed
    if (m_appIsRTL)
        isRTL = !isRTL;
#endif
    m_isRTL = isRTL;

    wxString langname = lang.IsValid() ? lang.DisplayName() : _("unknown language");
    wxListItem colInfo;
    colInfo.SetMask(wxLIST_MASK_TEXT);
    colInfo.SetText(wxString::Format(_(L"Translation — %s"), langname));
    colInfo.SetAlign(isRTL ? wxLIST_FORMAT_RIGHT : wxLIST_FORMAT_LEFT);
    SetColumn(1, colInfo);

    // sort catalog items, create indexes mapping
    CreateSortMap();

    // now that everything is prepared, we may set the item count
    SetItemCount(m_catalog->GetCount());

    // scroll to the top and refresh everything:
    if ( m_catalog->GetCount() )
    {
        SelectOnly(0);
        RefreshItems(0, m_catalog->GetCount()-1);
    }
    else
    {
        Refresh();
    }
}

void PoeditListCtrl::Sort()
{
    if ( m_catalog && m_catalog->GetCount() )
    {
        auto sel = GetSelectedCatalogItems();

        CreateSortMap();
        RefreshItems(0, m_catalog->GetCount()-1);

        SetSelectedCatalogItems(sel);
    }
    else
    {
        Refresh();
    }
}

void PoeditListCtrl::CreateSortMap()
{
    int count = (int)m_catalog->GetCount();

    m_mapListToCatalog.resize(count);
    m_mapCatalogToList.resize(count);

    // First create identity mapping for the sort order.
    for ( int i = 0; i < count; i++ )
        m_mapListToCatalog[i] = i;

    // m_mapListToCatalog will hold our desired sort order. Sort it in place
    // now, using the desired sort criteria.
    CatalogItemsComparator comparator(*m_catalog, sortOrder);
    std::sort
    (
        m_mapListToCatalog.begin(),
        m_mapListToCatalog.end(),
        std::ref(comparator)
    );

    // Finally, construct m_mapCatalogToList to be the inverse mapping to
    // m_mapListToCatalog.
    for ( int i = 0; i < count; i++ )
        m_mapCatalogToList[m_mapListToCatalog[i]] = i;
}

wxString PoeditListCtrl::OnGetItemText(long item, long column) const
{
    if (m_catalog == NULL)
        return wxEmptyString;

    const CatalogItem& d = ListIndexToCatalogItem((int)item);

    switch (column)
    {
        case 0:
        {
            wxString orig;
            if ( d.HasContext() )
                orig.Printf("%s  [ %s ]",
                            d.GetString().c_str(), d.GetContext().c_str());
            else
                orig = d.GetString();

            // Add RTL Unicode mark to render bidi texts correctly
            if (m_appIsRTL)
                return L"\u202a" + orig.substr(0,GetMaxColChars());
            else
                return orig.substr(0,GetMaxColChars());
        }
        case 1:
        {
            // Add RTL Unicode mark to render bidi texts correctly
            if (m_isRTL && !m_appIsRTL)
                return L"\u202b" + d.GetTranslation();
            else
                return d.GetTranslation();
        }
        case 2:
            return wxString() << d.GetId();

        default:
            return wxEmptyString;
    }
}

wxListItemAttr *PoeditListCtrl::OnGetItemAttr(long item) const
{
    long idx = item % 2;

    if (m_catalog == NULL)
        return (wxListItemAttr*)&m_attrNormal[idx];

    const CatalogItem& d = ListIndexToCatalogItem((int)item);

    if (d.GetValidity() == CatalogItem::Val_Invalid)
        return (wxListItemAttr*)&m_attrInvalid[idx];
    else if (!d.IsTranslated())
        return (wxListItemAttr*)&m_attrUntranslated[idx];
    else if (d.IsFuzzy())
        return (wxListItemAttr*)&m_attrFuzzy[idx];
    else
        return (wxListItemAttr*)&m_attrNormal[idx];
}

wxListItemAttr *PoeditListCtrl::OnGetItemColumnAttr(long item, long column) const
{
    if (column == 2)
    {
        return (wxListItemAttr*)&m_attrId;
    }
    else
    {
        return OnGetItemAttr(item);
    }
}

int PoeditListCtrl::OnGetItemImage(long item) const
{
    if (m_catalog == NULL)
        return IMG_NOTHING;

    const CatalogItem& d = ListIndexToCatalogItem((int)item);

    if (d.GetBookmark() != NO_BOOKMARK)
        return IMG_BOOKMARK;
    else if (d.HasComment() || d.HasExtractedComments())
        return IMG_COMMENT;
    else if (d.IsAutomatic())
        return IMG_AUTOMATIC;
    else
        return IMG_NOTHING;
}

void PoeditListCtrl::OnSize(wxSizeEvent& event)
{
    wxWindowUpdateLocker lock(this);

    SizeColumns();
    event.Skip();
}<commit_msg>Don't use poedit-status-nothing on Windows<commit_after>/*
 *  This file is part of Poedit (http://poedit.net)
 *
 *  Copyright (C) 1999-2015 Vaclav Slavik
 *  Copyright (C) 2005 Olivier Sannier
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a
 *  copy of this software and associated documentation files (the "Software"),
 *  to deal in the Software without restriction, including without limitation
 *  the rights to use, copy, modify, merge, publish, distribute, sublicense,
 *  and/or sell copies of the Software, and to permit persons to whom the
 *  Software is furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in
 *  all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 *  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 *  DEALINGS IN THE SOFTWARE.
 *
 */

#include "edlistctrl.h"

#include "hidpi.h"
#include "language.h"
#include "digits.h"
#include "cat_sorting.h"

#include <wx/wx.h>
#include <wx/imaglist.h>
#include <wx/artprov.h>
#include <wx/dcmemory.h>
#include <wx/image.h>
#include <wx/wupdlock.h>

#include <algorithm>

namespace
{

// how much to darken the other color in shaded list (this value
// is what GTK+ uses in its tree view control)
#define DARKEN_FACTOR      0.95

// max difference in color to consider it "amost" same if it differs by most
// this amount (in 0..255 range) from the tested color:
#define COLOR_SIMILARITY_FACTOR  20

inline bool IsAlmostBlack(const wxColour& clr)
{
    return (clr.Red() <= COLOR_SIMILARITY_FACTOR &&
            clr.Green() <= COLOR_SIMILARITY_FACTOR &&
            clr.Blue() <= COLOR_SIMILARITY_FACTOR);
}

inline bool IsAlmostWhite(const wxColour& clr)
{
    return (clr.Red() >= 255-COLOR_SIMILARITY_FACTOR &&
            clr.Green() >= 255-COLOR_SIMILARITY_FACTOR &&
            clr.Blue() >= 255-COLOR_SIMILARITY_FACTOR);
}

// colours used in the list:

const wxColour gs_ErrorColor("#ff5050");

// colors for white list control background
const wxColour gs_UntranslatedForWhite("#103f67");
const wxColour gs_FuzzyForWhite("#a9861b");

// ditto for black background
const wxColour gs_UntranslatedForBlack("#1962a0");
const wxColour gs_FuzzyForBlack("#a9861b");

const wxColour gs_TranspColor(254, 0, 253); // FIXME: get rid of this

// wxMSW doesn't need a dummy image to align columns properly, unlike wxOSX
#ifdef __WXMSW__
#define IMG_NOTHING -1
#endif

enum
{
#ifndef __WXMSW__
    IMG_NOTHING,
#endif
    IMG_AUTOMATIC,
    IMG_COMMENT,
    IMG_BOOKMARK
};

} // anonymous namespace

BEGIN_EVENT_TABLE(PoeditListCtrl, wxListCtrl)
   EVT_SIZE(PoeditListCtrl::OnSize)
END_EVENT_TABLE()

PoeditListCtrl::PoeditListCtrl(wxWindow *parent,
               wxWindowID id,
               const wxPoint &pos,
               const wxSize &size,
               long style,
               bool dispIDs,
               const wxValidator& validator,
               const wxString &name)
     : wxListView(parent, id, pos, size, style | wxLC_VIRTUAL, validator, name)
{
    m_catalog = NULL;
    m_displayIDs = dispIDs;

    m_isRTL = false;
    m_appIsRTL = (wxTheApp->GetLayoutDirection() == wxLayout_RightToLeft);

    sortOrder = SortOrder::Default();

    CreateColumns();

    wxImageList *list = new wxImageList(PX(16), PX(16));

    // IMG_XXX:
#ifndef __WXMSW__
    list->Add(wxArtProvider::GetBitmap("poedit-status-nothing"));
#endif
    list->Add(wxArtProvider::GetBitmap("poedit-status-automatic"));
    list->Add(wxArtProvider::GetBitmap("poedit-status-comment"));
    list->Add(wxArtProvider::GetBitmap("poedit-status-bookmark"));

    AssignImageList(list, wxIMAGE_LIST_SMALL);

    // configure items colors & fonts:

    wxVisualAttributes visual = GetDefaultAttributes();
    wxColour shaded = visual.colBg;

#ifdef __WXMSW__
    // On Windows 7, shaded list items make it impossible to see the selection,
    // so use different color for it (see bug #336).
    int verMaj, verMin;
    wxGetOsVersion(&verMaj, &verMin);
    if ( verMaj > 6 || (verMaj == 6 && verMin >= 1) )
    {
        shaded.Set(int(0.99 * shaded.Red()),
                   int(0.99 * shaded.Green()),
                   shaded.Blue());
    }
    else
#endif // __WXMSW__
#ifdef __WXOSX__
    if ( shaded == *wxWHITE )
    {
        // use standard shaded color from finder/databrowser:
        shaded.Set("#f0f5fd");
    }
    else
#endif // __WXOSX__
    {
        shaded.Set(int(DARKEN_FACTOR * shaded.Red()),
                   int(DARKEN_FACTOR * shaded.Green()),
                   int(DARKEN_FACTOR * shaded.Blue()));
    }

    m_attrNormal[1].SetBackgroundColour(shaded);
    m_attrUntranslated[1].SetBackgroundColour(shaded);
    m_attrFuzzy[1].SetBackgroundColour(shaded);
    m_attrInvalid[1].SetBackgroundColour(shaded);

    // FIXME: make this user-configurable
    if ( IsAlmostWhite(visual.colBg) )
    {
        m_attrUntranslated[0].SetTextColour(gs_UntranslatedForWhite);
        m_attrUntranslated[1].SetTextColour(gs_UntranslatedForWhite);
        m_attrFuzzy[0].SetTextColour(gs_FuzzyForWhite);
        m_attrFuzzy[1].SetTextColour(gs_FuzzyForWhite);
    }
    else if ( IsAlmostBlack(visual.colBg) )
    {
        m_attrUntranslated[0].SetTextColour(gs_UntranslatedForBlack);
        m_attrUntranslated[1].SetTextColour(gs_UntranslatedForBlack);
        m_attrFuzzy[0].SetTextColour(gs_FuzzyForBlack);
        m_attrFuzzy[1].SetTextColour(gs_FuzzyForBlack);
    }
    // else: we don't know if the default colors would be well-visible on
    //       user's background color, so play it safe and don't highlight
    //       anything

    // FIXME: todo; use appropriate font for fuzzy/trans/untrans
    m_attrInvalid[0].SetBackgroundColour(gs_ErrorColor);
    m_attrInvalid[1].SetBackgroundColour(gs_ErrorColor);

    // Use gray for IDs
    if ( IsAlmostBlack(visual.colFg) )
        m_attrId.SetTextColour(wxColour("#A1A1A1"));

    SetCustomFont(wxNullFont);
}

PoeditListCtrl::~PoeditListCtrl()
{
    sortOrder.Save();
}

void PoeditListCtrl::SetCustomFont(wxFont font_)
{
    wxFont font(font_);

    if ( !font.IsOk() )
        font = GetDefaultAttributes().font;

    m_attrNormal[0].SetFont(font);
    m_attrNormal[1].SetFont(font);

    wxFont fontb = font;
    fontb.SetWeight(wxFONTWEIGHT_BOLD);

    m_attrUntranslated[0].SetFont(fontb);
    m_attrUntranslated[1].SetFont(fontb);

    m_attrFuzzy[0].SetFont(fontb);
    m_attrFuzzy[1].SetFont(fontb);

    m_attrInvalid[0].SetFont(fontb);
    m_attrInvalid[1].SetFont(fontb);
}

void PoeditListCtrl::SetDisplayLines(bool dl)
{
    m_displayIDs = dl;
    CreateColumns();
}

void PoeditListCtrl::CreateColumns()
{
    DeleteAllColumns();
    InsertColumn(0, _("Source text"));
    InsertColumn(1, _("Translation"));
    if (m_displayIDs)
        InsertColumn(2, _("ID"), wxLIST_FORMAT_RIGHT);

#ifdef __WXMSW__
    if (m_appIsRTL)
    {
        // another wx quirk: if we truly need left alignment, we must lie under RTL locales
        wxListItem colInfoOrig;
        colInfoOrig.SetAlign(wxLIST_FORMAT_RIGHT);
        SetColumn(0, colInfoOrig);
    }
#endif

    SizeColumns();
}

void PoeditListCtrl::SizeColumns()
{
    const int LINE_COL_SIZE = m_displayIDs ? PX(50) : 0;

    int w = GetSize().x
            - wxSystemSettings::GetMetric(wxSYS_VSCROLL_X) - 10
            - LINE_COL_SIZE;
    SetColumnWidth(0, w / 2);
    SetColumnWidth(1, w - w / 2);
    if (m_displayIDs)
        SetColumnWidth(2, LINE_COL_SIZE);

    m_colWidth = (w/2) / GetCharWidth();
}

void PoeditListCtrl::CatalogChanged(Catalog* catalog)
{
    wxWindowUpdateLocker no_updates(this);

    const bool isSameCatalog = (catalog == m_catalog);

    std::vector<int> selection;
    if (isSameCatalog)
        selection = GetSelectedCatalogItems();

    // this is to prevent crashes (wxMac at least) when shortening virtual
    // listctrl when its scrolled to the bottom:
    m_catalog = nullptr;
    SetItemCount(0);

    // now read the new catalog:
    m_catalog = catalog;
    ReadCatalog();

    if (isSameCatalog && !selection.empty())
        SetSelectedCatalogItems(selection);
}

void PoeditListCtrl::ReadCatalog()
{
    wxWindowUpdateLocker no_updates(this);

    // clear the list and its sort order too:
    SetItemCount(0);
    m_mapListToCatalog.clear();
    m_mapCatalogToList.clear();

    if (m_catalog == NULL)
    {
        Refresh();
        return;
    }

    auto lang = m_catalog->GetLanguage();
    auto isRTL = lang.IsRTL();
#ifdef __WXMSW__
    // a quirk of wx API: if the current locale is RTL, the meaning of L and R is reversed
    if (m_appIsRTL)
        isRTL = !isRTL;
#endif
    m_isRTL = isRTL;

    wxString langname = lang.IsValid() ? lang.DisplayName() : _("unknown language");
    wxListItem colInfo;
    colInfo.SetMask(wxLIST_MASK_TEXT);
    colInfo.SetText(wxString::Format(_(L"Translation — %s"), langname));
    colInfo.SetAlign(isRTL ? wxLIST_FORMAT_RIGHT : wxLIST_FORMAT_LEFT);
    SetColumn(1, colInfo);

    // sort catalog items, create indexes mapping
    CreateSortMap();

    // now that everything is prepared, we may set the item count
    SetItemCount(m_catalog->GetCount());

    // scroll to the top and refresh everything:
    if ( m_catalog->GetCount() )
    {
        SelectOnly(0);
        RefreshItems(0, m_catalog->GetCount()-1);
    }
    else
    {
        Refresh();
    }
}

void PoeditListCtrl::Sort()
{
    if ( m_catalog && m_catalog->GetCount() )
    {
        auto sel = GetSelectedCatalogItems();

        CreateSortMap();
        RefreshItems(0, m_catalog->GetCount()-1);

        SetSelectedCatalogItems(sel);
    }
    else
    {
        Refresh();
    }
}

void PoeditListCtrl::CreateSortMap()
{
    int count = (int)m_catalog->GetCount();

    m_mapListToCatalog.resize(count);
    m_mapCatalogToList.resize(count);

    // First create identity mapping for the sort order.
    for ( int i = 0; i < count; i++ )
        m_mapListToCatalog[i] = i;

    // m_mapListToCatalog will hold our desired sort order. Sort it in place
    // now, using the desired sort criteria.
    CatalogItemsComparator comparator(*m_catalog, sortOrder);
    std::sort
    (
        m_mapListToCatalog.begin(),
        m_mapListToCatalog.end(),
        std::ref(comparator)
    );

    // Finally, construct m_mapCatalogToList to be the inverse mapping to
    // m_mapListToCatalog.
    for ( int i = 0; i < count; i++ )
        m_mapCatalogToList[m_mapListToCatalog[i]] = i;
}

wxString PoeditListCtrl::OnGetItemText(long item, long column) const
{
    if (m_catalog == NULL)
        return wxEmptyString;

    const CatalogItem& d = ListIndexToCatalogItem((int)item);

    switch (column)
    {
        case 0:
        {
            wxString orig;
            if ( d.HasContext() )
                orig.Printf("%s  [ %s ]",
                            d.GetString().c_str(), d.GetContext().c_str());
            else
                orig = d.GetString();

            // Add RTL Unicode mark to render bidi texts correctly
            if (m_appIsRTL)
                return L"\u202a" + orig.substr(0,GetMaxColChars());
            else
                return orig.substr(0,GetMaxColChars());
        }
        case 1:
        {
            // Add RTL Unicode mark to render bidi texts correctly
            if (m_isRTL && !m_appIsRTL)
                return L"\u202b" + d.GetTranslation();
            else
                return d.GetTranslation();
        }
        case 2:
            return wxString() << d.GetId();

        default:
            return wxEmptyString;
    }
}

wxListItemAttr *PoeditListCtrl::OnGetItemAttr(long item) const
{
    long idx = item % 2;

    if (m_catalog == NULL)
        return (wxListItemAttr*)&m_attrNormal[idx];

    const CatalogItem& d = ListIndexToCatalogItem((int)item);

    if (d.GetValidity() == CatalogItem::Val_Invalid)
        return (wxListItemAttr*)&m_attrInvalid[idx];
    else if (!d.IsTranslated())
        return (wxListItemAttr*)&m_attrUntranslated[idx];
    else if (d.IsFuzzy())
        return (wxListItemAttr*)&m_attrFuzzy[idx];
    else
        return (wxListItemAttr*)&m_attrNormal[idx];
}

wxListItemAttr *PoeditListCtrl::OnGetItemColumnAttr(long item, long column) const
{
    if (column == 2)
    {
        return (wxListItemAttr*)&m_attrId;
    }
    else
    {
        return OnGetItemAttr(item);
    }
}

int PoeditListCtrl::OnGetItemImage(long item) const
{
    if (m_catalog == NULL)
        return IMG_NOTHING;

    const CatalogItem& d = ListIndexToCatalogItem((int)item);

    if (d.GetBookmark() != NO_BOOKMARK)
        return IMG_BOOKMARK;
    else if (d.HasComment() || d.HasExtractedComments())
        return IMG_COMMENT;
    else if (d.IsAutomatic())
        return IMG_AUTOMATIC;
    else
        return IMG_NOTHING;
}

void PoeditListCtrl::OnSize(wxSizeEvent& event)
{
    wxWindowUpdateLocker lock(this);

    SizeColumns();
    event.Skip();
}<|endoftext|>


<commit_before>#include <core/mat4.h>
#include "core/draw.h"
#include "core/random.h"
#include "core/shufflebag.h"
#include "core/mat4.h"
#include "core/axisangle.h"
#include "core/aabb.h"
#include "core/texturetypes.h"
#include "core/vfs.h"
#include "core/vfs_imagegenerator.h"
#include "core/vfs_path.h"
#include "core/os.h"
#include "core/range.h"
#include "core/camera.h"
#include "core/stringutils.h"
#include "core/stdutils.h"
#include "core/proto.h"
#include "core/log.h"

#include "core/path.h"

#include <render/init.h>
#include <render/debuggl.h>
#include <render/materialshader.h>
#include <render/compiledmesh.h>
#include <render/texturecache.h>
#include "render/shaderattribute3d.h"
#include "render/texture.h"
#include "render/world.h"
#include "render/viewport.h"
#include "render/materialshadercache.h"
#include "render/defaultfiles.h"

#include "window/imguilibrary.h"
#include "window/timer.h"
#include "window/imgui_ext.h"
#include "window/fpscontroller.h"
#include "window/sdllibrary.h"
#include "window/sdlwindow.h"
#include "window/sdlglcontext.h"
#include "window/filesystem.h"
#include "window/engine.h"

#include "imgui/imgui.h"
#include <SDL.h>
#include <iostream>
#include <memory>

#include "painter/canvas.h"

#define IMGUI_DEFINE_MATH_OPERATORS
#include "imgui/imgui_internal.h"
#include "window/imgui_ext.h"

LOG_SPECIFY_DEFAULT_LOGGER("painter")

int
main(int argc, char** argv)
{
  Engine engine;

  if(engine.Setup() == false)
  {
    return -1;
  }

  int window_width  = 1280;
  int window_height = 720;

  if(!engine.CreateWindow("Painter", window_width, window_height, true))
  {
    return -1;
  }

  // ViewportHandler viewport_handler;
  // viewport_handler.SetSize(window_width, window_height);

  bool running = true;

  //////////////////////////////////////////////////////////////////////////////
  // main loop
  CanvasConfig cc;
  Canvas canvas;
  BezierPath2 path (point2f(0,0));
  int index = -1;

  while(running)
  {
    SDL_Event e;
    while(SDL_PollEvent(&e) != 0)
    {
      engine.imgui->ProcessEvents(&e);

      if(engine.HandleResize(e, &window_width, &window_height))
      {
        // viewport_handler.SetSize(window_width, window_height);
      }

      switch(e.type)
      {
        case SDL_QUIT:
          running = false;
          break;
        default:
          // ignore other events
          break;
      }
    }

    engine.imgui->StartNewFrame();

    if(ImGui::BeginMainMenuBar())
    {
      if(ImGui::BeginMenu("File"))
      {
        if(ImGui::MenuItem("Exit", "Ctrl+Q"))
        {
          running = false;
        }
        ImGui::EndMenu();
      }
    }
    ImGui::EndMainMenuBar();

    ImGui::SetNextWindowSize(ImVec2{300, 300}, ImGuiCond_FirstUseEver);
    if(ImGui::Begin("painter"))
    {
      // win->Run(&style_data);
      canvas.Begin(cc);
      canvas.ShowGrid(cc);

      if(ImGui::IsMouseReleased(0)) { index = -1; }
      auto handle = [&canvas, &index](const ImVec2& p, int id, ImU32 color)
      {
        const auto size = 5.0f;
        ImDrawList* draw_list = ImGui::GetWindowDrawList();
        const auto sp = canvas.WorldToScreen(p);
        const auto me = ImGui::GetMousePos();
        const auto hover = vec2f::FromTo(C(me),C(sp)).GetLengthSquared() < size*size;
        if(index==-1 && hover && ImGui::IsMouseDown(0))
        {
          index = id;
        }
        draw_list->AddCircleFilled(sp, size, color);
        if(index==id)
        {
          // capture current drag item...
          if(ImGui::IsMouseDragging())
          {
            const auto d = ImGui::GetMouseDragDelta();
            ImGui::ResetMouseDragDelta();
            // todo: handle scale/zoom
            return std::make_pair(true, vec2f(d.x, d.y) / canvas.view.scale );
          }
        }
        return std::make_pair(false, vec2f(0,0));
      };

      int i = 0;
      for(auto& p: path.points)
      {
        i += 1;
        auto r = handle(C(p), i, IM_COL32(200, 100, 20, 200));
        if(r.first)
        {
          p += r.second;
        }
      }

      canvas.ShowRuler(cc);
      canvas.End(cc);
    }
    ImGui::End();

    // ImGui::ShowMetricsWindow();

    engine.init->ClearScreen(Color::Wheat);
    engine.imgui->Render();

    SDL_GL_SwapWindow(engine.window->window);
  }

  return 0;
}

<commit_msg>bezier kinda working<commit_after>#include <core/mat4.h>
#include "core/draw.h"
#include "core/random.h"
#include "core/shufflebag.h"
#include "core/mat4.h"
#include "core/axisangle.h"
#include "core/aabb.h"
#include "core/texturetypes.h"
#include "core/vfs.h"
#include "core/vfs_imagegenerator.h"
#include "core/vfs_path.h"
#include "core/os.h"
#include "core/range.h"
#include "core/camera.h"
#include "core/stringutils.h"
#include "core/stdutils.h"
#include "core/proto.h"
#include "core/log.h"

#include "core/path.h"

#include <render/init.h>
#include <render/debuggl.h>
#include <render/materialshader.h>
#include <render/compiledmesh.h>
#include <render/texturecache.h>
#include "render/shaderattribute3d.h"
#include "render/texture.h"
#include "render/world.h"
#include "render/viewport.h"
#include "render/materialshadercache.h"
#include "render/defaultfiles.h"

#include "window/imguilibrary.h"
#include "window/timer.h"
#include "window/imgui_ext.h"
#include "window/fpscontroller.h"
#include "window/sdllibrary.h"
#include "window/sdlwindow.h"
#include "window/sdlglcontext.h"
#include "window/filesystem.h"
#include "window/engine.h"

#include "imgui/imgui.h"
#include <SDL.h>
#include <iostream>
#include <memory>

#include "painter/canvas.h"

#define IMGUI_DEFINE_MATH_OPERATORS
#include "imgui/imgui_internal.h"
#include "window/imgui_ext.h"

LOG_SPECIFY_DEFAULT_LOGGER("painter")

int
main(int argc, char** argv)
{
  Engine engine;

  if(engine.Setup() == false)
  {
    return -1;
  }

  int window_width  = 1280;
  int window_height = 720;

  if(!engine.CreateWindow("Painter", window_width, window_height, true))
  {
    return -1;
  }

  // ViewportHandler viewport_handler;
  // viewport_handler.SetSize(window_width, window_height);

  bool running = true;

  //////////////////////////////////////////////////////////////////////////////
  // main loop
  CanvasConfig cc;
  Canvas canvas;
  BezierPath2 path (point2f(0,0));
  int index = -1;

  while(running)
  {
    SDL_Event e;
    while(SDL_PollEvent(&e) != 0)
    {
      engine.imgui->ProcessEvents(&e);

      if(engine.HandleResize(e, &window_width, &window_height))
      {
        // viewport_handler.SetSize(window_width, window_height);
      }

      switch(e.type)
      {
        case SDL_QUIT:
          running = false;
          break;
        default:
          // ignore other events
          break;
      }
    }

    engine.imgui->StartNewFrame();

    if(ImGui::BeginMainMenuBar())
    {
      if(ImGui::BeginMenu("File"))
      {
        if(ImGui::MenuItem("Exit", "Ctrl+Q"))
        {
          running = false;
        }
        ImGui::EndMenu();
      }
    }
    ImGui::EndMainMenuBar();

    ImGui::SetNextWindowSize(ImVec2{300, 300}, ImGuiCond_FirstUseEver);
    if(ImGui::Begin("painter"))
    {
      // win->Run(&style_data);
      canvas.Begin(cc);
      canvas.ShowGrid(cc);

      if(ImGui::IsMouseReleased(0)) { index = -1; }
      auto handle = [&canvas, &index](const ImVec2& p, int id, ImU32 color)
      {
        const auto size = 5.0f;
        ImDrawList* draw_list = ImGui::GetWindowDrawList();
        const auto sp = canvas.WorldToScreen(p);
        const auto me = ImGui::GetMousePos();
        const auto hover = vec2f::FromTo(C(me),C(sp)).GetLengthSquared() < size*size;
        if(index==-1 && hover && ImGui::IsMouseDown(0))
        {
          index = id;
        }
        draw_list->AddCircleFilled(sp, size, color);
        if(index==id)
        {
          // capture current drag item...
          if(ImGui::IsMouseDragging())
          {
            const auto d = ImGui::GetMouseDragDelta();
            ImGui::ResetMouseDragDelta();
            // todo: handle scale/zoom
            return std::make_pair(true, vec2f(d.x, d.y) / canvas.view.scale );
          }
        }
        return std::make_pair(false, vec2f(0,0));
      };
      auto line = [](const ImVec2& a, const ImVec2& b, ImU32 color)
      {
        ImDrawList* draw_list = ImGui::GetWindowDrawList();
        draw_list->PathLineTo(a);
        draw_list->PathLineTo(b);
        draw_list->PathStroke(color, false);
      };

      const auto curve_color = IM_COL32(0, 0, 200, 255);
      const auto line_color = IM_COL32(0, 0, 0, 255);

      // draw handles
      int i = 0;
      for(auto& p: path.points)
      {
        i += 1;
        auto r = handle(C(p), i, IM_COL32(200, 100, 20, 200));
        if(r.first)
        {
          p += r.second;
        }
      }
      // draw bezier and link lines
      const auto tseg = path.GetNumberOfSegments();
      for(size_t seg=0; seg<tseg; seg+=1)
      {
        auto s = path.GetPointsInSegment(seg);
        auto* dl = ImGui::GetWindowDrawList();
        dl->AddBezierCurve(canvas.WorldToScreen(C(s[0])), canvas.WorldToScreen(C(s[1])), canvas.WorldToScreen(C(s[3])), canvas.WorldToScreen(C(s[2])), curve_color, 1);
        line(canvas.WorldToScreen(C(s[0])), canvas.WorldToScreen(C(s[1])), line_color);
        line(canvas.WorldToScreen(C(s[2])), canvas.WorldToScreen(C(s[3])), line_color);
      }

      canvas.ShowRuler(cc);
      canvas.End(cc);
    }
    ImGui::End();

    // ImGui::ShowMetricsWindow();

    engine.init->ClearScreen(Color::Wheat);
    engine.imgui->Render();

    SDL_GL_SwapWindow(engine.window->window);
  }

  return 0;
}

<|endoftext|>


<commit_before>#include "editormanager.h"

#include <QtCore/QPluginLoader>
#include <QtCore/QCoreApplication>
#include <QtGui/QMessageBox>
#include <QtGui/QIcon>

#include <QtCore/QtDebug>

#include "editorinterface.h"
#include "../kernel/ids.h"

using namespace qReal;

EditorManager::EditorManager(QObject *parent)
	: QObject(parent), mRoot()
{
	//    foreach (QObject *plugin, QPluginLoader::staticInstances())
	//        populateMenus(plugin);

	mPluginsDir = QDir(qApp->applicationDirPath());

	while (!mPluginsDir.isRoot() && !mPluginsDir.entryList(QDir::Dirs).contains("plugins")) {
		mPluginsDir.cdUp();
	}

	mPluginsDir.cd("plugins");

	foreach (QString fileName, mPluginsDir.entryList(QDir::Files)) {
		QPluginLoader loader(mPluginsDir.absoluteFilePath(fileName));
		QObject *plugin = loader.instance();

		if (plugin) {
			EditorInterface *iEditor = qobject_cast<EditorInterface *>(plugin);
			if (iEditor) {
				foreach (QString d, iEditor->diagrams())
					foreach (QString e, iEditor->elements(d))
					;

				mPluginsLoaded += iEditor->id();
				mPluginIface[iEditor->id()] = iEditor;
			}
		} else {
			qDebug() << "Plugin loading failed: " << loader.errorString();
			// Keep silent.
			// QMessageBox::warning(0, "QReal Plugin", loader.errorString() );
		}
	}
}

QList<Id> EditorManager::editors() const
{
	QList<Id> editors;
	foreach (QString e, mPluginsLoaded) {
		editors.append(Id(e));
	}
	return editors;
}

QList<Id> EditorManager::diagrams(const Id &editor) const
{
	QList<Id> diagrams;
	Q_ASSERT(mPluginsLoaded.contains(editor.editor()));

	foreach (QString e, mPluginIface[editor.editor()]->diagrams()) {
		diagrams.append(Id(editor, e));
	}
	return diagrams;
}

QList<Id> EditorManager::elements(const Id &diagram) const
{
	QList<Id> elements;
	Q_ASSERT(mPluginsLoaded.contains(diagram.editor()));

	foreach (QString e, mPluginIface[diagram.editor()]->elements(diagram.diagram())) {
		elements.append(Id(diagram, e));
	}
	return elements;
}

bool EditorManager::isEditor(const Id &id) const
{
	Q_ASSERT(mPluginsLoaded.contains(id.editor()));
	return id.idSize() == 1;
}

bool EditorManager::isDiagram(const Id &id) const
{
	Q_ASSERT(mPluginsLoaded.contains(id.editor()));
	return id.idSize() == 2;
}

bool EditorManager::isElement(const Id &id) const
{
	Q_ASSERT(mPluginsLoaded.contains(id.editor()));
	return id.idSize() == 3;
}

QString EditorManager::friendlyName(const Id &id) const
{
	Q_ASSERT(mPluginsLoaded.contains(id.editor()));

	switch (id.idSize()) {
		case 2:
			return mPluginIface[id.editor()]->editorName();
		case 3:
			return mPluginIface[id.editor()]->diagramName(id.diagram());
		case 4:
			return mPluginIface[id.editor()]->elementName(id.diagram(), id.element());
		default:
			Q_ASSERT(!"Malformed Id");
			return "";
	}
}

QIcon EditorManager::icon(const Id &id) const
{
	Q_ASSERT(mPluginsLoaded.contains(id.editor()));
	return mPluginIface[id.editor()]->getIcon(id.diagram(), id.element());
}

UML::Element* EditorManager::graphicalObject(const Id &id) const
{
	Q_ASSERT(mPluginsLoaded.contains(id.editor()));
	return mPluginIface[id.editor()]->getGraphicalObject(id.diagram(), id.element());
}
<commit_msg>Исправлен баг с friendly-именами.<commit_after>#include "editormanager.h"

#include <QtCore/QPluginLoader>
#include <QtCore/QCoreApplication>
#include <QtGui/QMessageBox>
#include <QtGui/QIcon>

#include <QtCore/QtDebug>

#include "editorinterface.h"
#include "../kernel/ids.h"

using namespace qReal;

EditorManager::EditorManager(QObject *parent)
	: QObject(parent), mRoot()
{
	//    foreach (QObject *plugin, QPluginLoader::staticInstances())
	//        populateMenus(plugin);

	mPluginsDir = QDir(qApp->applicationDirPath());

	while (!mPluginsDir.isRoot() && !mPluginsDir.entryList(QDir::Dirs).contains("plugins")) {
		mPluginsDir.cdUp();
	}

	mPluginsDir.cd("plugins");

	foreach (QString fileName, mPluginsDir.entryList(QDir::Files)) {
		QPluginLoader loader(mPluginsDir.absoluteFilePath(fileName));
		QObject *plugin = loader.instance();

		if (plugin) {
			EditorInterface *iEditor = qobject_cast<EditorInterface *>(plugin);
			if (iEditor) {
				foreach (QString d, iEditor->diagrams())
					foreach (QString e, iEditor->elements(d))
					;

				mPluginsLoaded += iEditor->id();
				mPluginIface[iEditor->id()] = iEditor;
			}
		} else {
			qDebug() << "Plugin loading failed: " << loader.errorString();
			// Keep silent.
			// QMessageBox::warning(0, "QReal Plugin", loader.errorString() );
		}
	}
}

QList<Id> EditorManager::editors() const
{
	QList<Id> editors;
	foreach (QString e, mPluginsLoaded) {
		editors.append(Id(e));
	}
	return editors;
}

QList<Id> EditorManager::diagrams(const Id &editor) const
{
	QList<Id> diagrams;
	Q_ASSERT(mPluginsLoaded.contains(editor.editor()));

	foreach (QString e, mPluginIface[editor.editor()]->diagrams()) {
		diagrams.append(Id(editor, e));
	}
	return diagrams;
}

QList<Id> EditorManager::elements(const Id &diagram) const
{
	QList<Id> elements;
	Q_ASSERT(mPluginsLoaded.contains(diagram.editor()));

	foreach (QString e, mPluginIface[diagram.editor()]->elements(diagram.diagram())) {
		elements.append(Id(diagram, e));
	}
	return elements;
}

bool EditorManager::isEditor(const Id &id) const
{
	Q_ASSERT(mPluginsLoaded.contains(id.editor()));
	return id.idSize() == 1;
}

bool EditorManager::isDiagram(const Id &id) const
{
	Q_ASSERT(mPluginsLoaded.contains(id.editor()));
	return id.idSize() == 2;
}

bool EditorManager::isElement(const Id &id) const
{
	Q_ASSERT(mPluginsLoaded.contains(id.editor()));
	return id.idSize() == 3;
}

QString EditorManager::friendlyName(const Id &id) const
{
	Q_ASSERT(mPluginsLoaded.contains(id.editor()));

	switch (id.idSize()) {
		case 1:
			return mPluginIface[id.editor()]->editorName();
		case 2:
			return mPluginIface[id.editor()]->diagramName(id.diagram());
		case 3:
			return mPluginIface[id.editor()]->elementName(id.diagram(), id.element());
		default:
			Q_ASSERT(!"Malformed Id");
			return "";
	}
}

QIcon EditorManager::icon(const Id &id) const
{
	Q_ASSERT(mPluginsLoaded.contains(id.editor()));
	return mPluginIface[id.editor()]->getIcon(id.diagram(), id.element());
}

UML::Element* EditorManager::graphicalObject(const Id &id) const
{
	Q_ASSERT(mPluginsLoaded.contains(id.editor()));
	return mPluginIface[id.editor()]->getGraphicalObject(id.diagram(), id.element());
}
<|endoftext|>


<commit_before>// RUN: %clang_cc1 -fsyntax-only -verify %s -fblocks

extern "C" int exit(int);

typedef struct {
    unsigned long ps[30];
    int qs[30];
} BobTheStruct;

int main (int argc, const char * argv[]) {
    BobTheStruct inny;
    BobTheStruct outty;
    BobTheStruct (^copyStruct)(BobTheStruct);
    int i;

    for(i=0; i<30; i++) {
        inny.ps[i] = i * i * i;
        inny.qs[i] = -i * i * i;
    }

    copyStruct = ^(BobTheStruct aBigStruct){ return aBigStruct; };  // pass-by-value intrinsically copies the argument

    outty = copyStruct(inny);

    if ( &inny == &outty ) {
        exit(1);
    }
    for(i=0; i<30; i++) {
        if ( (inny.ps[i] != outty.ps[i]) || (inny.qs[i] != outty.qs[i]) ) {
            exit(1);
        }
    }

    return 0;
}

namespace rdar8134521 {
  void foo() {
    int (^P)(int) = reinterpret_cast<int(^)(int)>(1);
    P = (int(^)(int))(1);

    P = reinterpret_cast<int(^)(int)>((void*)1);
    P = (int(^)(int))((void*)1);
  }
}
<commit_msg>Test case for rdar://problem/11055105, a bug with the instantiation of references to function template parameters in noexcept clauses when the instantiation is forced from a point during parsing when a block is in scope.<commit_after>// RUN: %clang_cc1 -fsyntax-only -verify %s -fblocks -std=c++11

extern "C" int exit(int);

typedef struct {
    unsigned long ps[30];
    int qs[30];
} BobTheStruct;

int main (int argc, const char * argv[]) {
    BobTheStruct inny;
    BobTheStruct outty;
    BobTheStruct (^copyStruct)(BobTheStruct);
    int i;

    for(i=0; i<30; i++) {
        inny.ps[i] = i * i * i;
        inny.qs[i] = -i * i * i;
    }

    copyStruct = ^(BobTheStruct aBigStruct){ return aBigStruct; };  // pass-by-value intrinsically copies the argument

    outty = copyStruct(inny);

    if ( &inny == &outty ) {
        exit(1);
    }
    for(i=0; i<30; i++) {
        if ( (inny.ps[i] != outty.ps[i]) || (inny.qs[i] != outty.qs[i]) ) {
            exit(1);
        }
    }

    return 0;
}

namespace rdar8134521 {
  void foo() {
    int (^P)(int) = reinterpret_cast<int(^)(int)>(1);
    P = (int(^)(int))(1);

    P = reinterpret_cast<int(^)(int)>((void*)1);
    P = (int(^)(int))((void*)1);
  }
}

namespace rdar11055105 {
  struct A {
    void foo();
  };

  template <class T> void foo(T &x) noexcept(noexcept(x.foo()));

  void (^block)() = ^{
    A a;
    foo(a);
  };
}
<|endoftext|>


<commit_before>/***********************************************************************************
** MIT License                                                                    **
**                                                                                **
** Copyright (c) 2017 Victor DENIS (victordenis01@gmail.com)                      **
**                                                                                **
** Permission is hereby granted, free of charge, to any person obtaining a copy   **
** of this software and associated documentation files (the "Software"), to deal  **
** in the Software without restriction, including without limitation the rights   **
** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell      **
** copies of the Software, and to permit persons to whom the Software is          **
** furnished to do so, subject to the following conditions:                       **
**                                                                                **
** The above copyright notice and this permission notice shall be included in all **
** copies or substantial portions of the Software.                                **
**                                                                                **
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR     **
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,       **
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE    **
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER         **
** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,  **
** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE  **
** SOFTWARE.                                                                      **
***********************************************************************************/

#include "Widgets/Tab/TabIcon.hpp"

#include <QPainter>
#include <QRect>

#include "Web/Tab/WebTab.hpp"

namespace Sn {

static const int ANIMATION_INTERVAL = 70;

TabIcon::Data* TabIcon::s_data = Q_NULLPTR;

TabIcon::TabIcon(QWidget* parent) :
	QWidget(parent),
	m_tab(nullptr),
	m_currentFrame(0),
	m_animationRunning(false),
	m_audioIconDisplayed(false)
{
	if (!s_data) {
		s_data = new TabIcon::Data;

		s_data->animationPixmap = QIcon(QLatin1String(":icon/tabs/loading.png")).pixmap(288, 16);
		s_data->framesCount = s_data->animationPixmap.width() / s_data->animationPixmap.height();
		s_data->audioPlayingPixmap =
			QIcon::fromTheme(QLatin1String("audio-volume-high"), QIcon(QLatin1String(":icons/tabs/audioplaying.svg")))
				.pixmap(16);
		s_data->audioMutedPixmap =
			QIcon::fromTheme(QLatin1String("audio-volume-muted"), QIcon(QLatin1String(":icons/tabs/audiomuted.svg")))
				.pixmap(16);
	}

	m_updateTimer = new QTimer(this);
	m_updateTimer->setInterval(ANIMATION_INTERVAL);

	m_hideTimer = new QTimer(this);
	m_hideTimer->setInterval(250);

	connect(m_updateTimer, &QTimer::timeout, this, &TabIcon::updateAnimationFrame);
	connect(m_hideTimer, &QTimer::timeout, this, &TabIcon::hide);

	resize(16, 16);
}

void TabIcon::setWebTab(WebTab* tab)
{
	m_tab = tab;

	//TODO: connection with webview by the tab

	updateIcon();
}

void TabIcon::updateIcon()
{
	m_sitePixmap = m_tab->icon(true).pixmap(16);

	if (m_sitePixmap.isNull())
		m_hideTimer->start();
	else
		showNormal();

	update();
}

void TabIcon::showLoadingAnimation()
{
	m_currentFrame = 0;

	updateAnimationFrame();
	show();
}

void TabIcon::hideLoadingAnimation()
{
	m_animationRunning = false;

	m_updateTimer->stop();
	updateIcon();
}

void TabIcon::updateAudioIcon(bool recentlyAudible)
{
	if (m_tab->isMuted() || (!m_tab->isMuted() && recentlyAudible)) {
		setToolTip(m_tab->isMuted() ? tr("Unmut Tab") : tr("Mute tab"));
		m_audioIconDisplayed = true;
		show();
	}
	else {
		setToolTip(QString());
		m_audioIconDisplayed = false;
		hide();
	}

	update();
}

void TabIcon::updateAnimationFrame()
{
	if (!m_animationRunning) {
		m_updateTimer->start();
		m_animationRunning = true;
	}

	update();
	m_currentFrame = (m_currentFrame + 1) % s_data->framesCount;
}

void TabIcon::show()
{
	if (!shouldBeVisible())
		return;

	m_hideTimer->stop();

	if (isVisible())
		return;

	setFixedSize(16, 16);
	emit resized();

	QWidget::show();
}

void TabIcon::hide()
{
	if (shouldBeVisible() || isHidden())
		return;

	setFixedSize(0, 0);
	emit resized();

	QWidget::hide();
}

bool TabIcon::shouldBeVisible() const
{
	return (!m_sitePixmap.isNull() || m_animationRunning || m_audioIconDisplayed);
}

void TabIcon::paintEvent(QPaintEvent* event)
{
	Q_UNUSED(event);

	QPainter painter{this};

	const int size{16};
	const int pixmapSize{qRound(size * s_data->animationPixmap.devicePixelRatio())};

	QRect r = rect();
	r.setX((r.width() - size) / 2);
	r.setY((r.height() - size) / 2);
	r.setWidth(size);
	r.setHeight(size);

	if (m_animationRunning)
		painter
			.drawPixmap(r, s_data->animationPixmap, QRect(m_currentFrame * pixmapSize, 0, pixmapSize, pixmapSize));
	else if (m_audioIconDisplayed)
		painter.drawPixmap(r, m_tab->isMuted() ? s_data->audioMutedPixmap : s_data->audioPlayingPixmap);
	else
		painter.drawPixmap(r, m_sitePixmap);

}

void TabIcon::mousePressEvent(QMouseEvent* event)
{
	if (m_audioIconDisplayed && event->button() == Qt::LeftButton)
		m_tab->toggleMuted();
	else
		QWidget::mousePressEvent(event);
}

}<commit_msg>Core/Widgets->Tab: [Fix] division by 0 in TabIcon<commit_after>/***********************************************************************************
** MIT License                                                                    **
**                                                                                **
** Copyright (c) 2017 Victor DENIS (victordenis01@gmail.com)                      **
**                                                                                **
** Permission is hereby granted, free of charge, to any person obtaining a copy   **
** of this software and associated documentation files (the "Software"), to deal  **
** in the Software without restriction, including without limitation the rights   **
** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell      **
** copies of the Software, and to permit persons to whom the Software is          **
** furnished to do so, subject to the following conditions:                       **
**                                                                                **
** The above copyright notice and this permission notice shall be included in all **
** copies or substantial portions of the Software.                                **
**                                                                                **
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR     **
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,       **
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE    **
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER         **
** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,  **
** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE  **
** SOFTWARE.                                                                      **
***********************************************************************************/

#include "Widgets/Tab/TabIcon.hpp"

#include <QPainter>
#include <QRect>

#include "Web/Tab/WebTab.hpp"

namespace Sn {

static const int ANIMATION_INTERVAL = 70;

TabIcon::Data* TabIcon::s_data = Q_NULLPTR;

TabIcon::TabIcon(QWidget* parent) :
	QWidget(parent),
	m_tab(nullptr),
	m_currentFrame(0),
	m_animationRunning(false),
	m_audioIconDisplayed(false)
{
	if (!s_data) {
		s_data = new TabIcon::Data;

		s_data->animationPixmap = QIcon(QLatin1String(":icons/tabs/loading.png")).pixmap(288, 16);
		s_data->framesCount = s_data->animationPixmap.width() / s_data->animationPixmap.height();
		s_data->audioPlayingPixmap =
			QIcon::fromTheme(QLatin1String("audio-volume-high"), QIcon(QLatin1String(":icons/tabs/audioplaying.svg")))
				.pixmap(16);
		s_data->audioMutedPixmap =
			QIcon::fromTheme(QLatin1String("audio-volume-muted"), QIcon(QLatin1String(":icons/tabs/audiomuted.svg")))
				.pixmap(16);
	}

	m_updateTimer = new QTimer(this);
	m_updateTimer->setInterval(ANIMATION_INTERVAL);

	m_hideTimer = new QTimer(this);
	m_hideTimer->setInterval(250);

	connect(m_updateTimer, &QTimer::timeout, this, &TabIcon::updateAnimationFrame);
	connect(m_hideTimer, &QTimer::timeout, this, &TabIcon::hide);

	resize(16, 16);
}

void TabIcon::setWebTab(WebTab* tab)
{
	m_tab = tab;

	//TODO: connection with webview by the tab

	updateIcon();
}

void TabIcon::updateIcon()
{
	m_sitePixmap = m_tab->icon(true).pixmap(16);

	if (m_sitePixmap.isNull())
		m_hideTimer->start();
	else
		showNormal();

	update();
}

void TabIcon::showLoadingAnimation()
{
	m_currentFrame = 0;

	updateAnimationFrame();
	show();
}

void TabIcon::hideLoadingAnimation()
{
	m_animationRunning = false;

	m_updateTimer->stop();
	updateIcon();
}

void TabIcon::updateAudioIcon(bool recentlyAudible)
{
	if (m_tab->isMuted() || (!m_tab->isMuted() && recentlyAudible)) {
		setToolTip(m_tab->isMuted() ? tr("Unmut Tab") : tr("Mute tab"));
		m_audioIconDisplayed = true;
		show();
	}
	else {
		setToolTip(QString());
		m_audioIconDisplayed = false;
		hide();
	}

	update();
}

void TabIcon::updateAnimationFrame()
{
	if (!m_animationRunning) {
		m_updateTimer->start();
		m_animationRunning = true;
	}

	update();
	m_currentFrame = (m_currentFrame + 1) % s_data->framesCount;
}

void TabIcon::show()
{
	if (!shouldBeVisible())
		return;

	m_hideTimer->stop();

	if (isVisible())
		return;

	setFixedSize(16, 16);
	emit resized();

	QWidget::show();
}

void TabIcon::hide()
{
	if (shouldBeVisible() || isHidden())
		return;

	setFixedSize(0, 0);
	emit resized();

	QWidget::hide();
}

bool TabIcon::shouldBeVisible() const
{
	return (!m_sitePixmap.isNull() || m_animationRunning || m_audioIconDisplayed);
}

void TabIcon::paintEvent(QPaintEvent* event)
{
	Q_UNUSED(event);

	QPainter painter{this};

	const int size{16};
	const int pixmapSize{qRound(size * s_data->animationPixmap.devicePixelRatio())};

	QRect r = rect();
	r.setX((r.width() - size) / 2);
	r.setY((r.height() - size) / 2);
	r.setWidth(size);
	r.setHeight(size);

	if (m_animationRunning)
		painter
			.drawPixmap(r, s_data->animationPixmap, QRect(m_currentFrame * pixmapSize, 0, pixmapSize, pixmapSize));
	else if (m_audioIconDisplayed)
		painter.drawPixmap(r, m_tab->isMuted() ? s_data->audioMutedPixmap : s_data->audioPlayingPixmap);
	else
		painter.drawPixmap(r, m_sitePixmap);

}

void TabIcon::mousePressEvent(QMouseEvent* event)
{
	if (m_audioIconDisplayed && event->button() == Qt::LeftButton)
		m_tab->toggleMuted();
	else
		QWidget::mousePressEvent(event);
}

}<|endoftext|>


<commit_before>// Copyright (c) 2019-2020 The Dash Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <evo/mnauth.h>

#include <evo/deterministicmns.h>
#include <llmq/quorums_utils.h>
#include <masternode/activemasternode.h>
#include <masternode/masternode-meta.h>
#include <masternode/masternode-sync.h>
#include <net.h>
#include <net_processing.h>
#include <netmessagemaker.h>
#include <validation.h>

#include <unordered_set>

void CMNAuth::PushMNAUTH(CNode* pnode, CConnman& connman)
{
    if (!fMasternodeMode || activeMasternodeInfo.proTxHash.IsNull()) {
        return;
    }

    uint256 signHash;
    {
        LOCK(pnode->cs_mnauth);
        if (pnode->receivedMNAuthChallenge.IsNull()) {
            return;
        }
        // We include fInbound in signHash to forbid interchanging of challenges by a man in the middle (MITM). This way
        // we protect ourselves against MITM in this form:
        //   node1 <- Eve -> node2
        // It does not protect against:
        //   node1 -> Eve -> node2
        // This is ok as we only use MNAUTH as a DoS protection and not for sensitive stuff
        signHash = ::SerializeHash(std::make_tuple(*activeMasternodeInfo.blsPubKeyOperator, pnode->receivedMNAuthChallenge, pnode->fInbound));
    }

    CMNAuth mnauth;
    mnauth.proRegTxHash = activeMasternodeInfo.proTxHash;
    mnauth.sig = activeMasternodeInfo.blsKeyOperator->Sign(signHash);

    LogPrint(BCLog::NET, "CMNAuth::%s -- Sending MNAUTH, peer=%d\n", __func__, pnode->GetId());

    connman.PushMessage(pnode, CNetMsgMaker(pnode->GetSendVersion()).Make(NetMsgType::MNAUTH, mnauth));
}

void CMNAuth::ProcessMessage(CNode* pnode, const std::string& strCommand, CDataStream& vRecv, CConnman& connman)
{
    if (!masternodeSync.IsBlockchainSynced()) {
        // we can't verify MNAUTH messages when we don't have the latest MN list
        return;
    }

    if (strCommand == NetMsgType::MNAUTH) {
        CMNAuth mnauth;
        vRecv >> mnauth;

        // only one MNAUTH allowed
        bool fAlreadyHaveMNAUTH = false;
        {
            LOCK(pnode->cs_mnauth);
            fAlreadyHaveMNAUTH = !pnode->verifiedProRegTxHash.IsNull();
        }
        if (fAlreadyHaveMNAUTH) {
            LOCK(cs_main);
            Misbehaving(pnode->GetId(), 100, "duplicate mnauth");
            return;
        }

        if ((~pnode->nServices) & (NODE_NETWORK | NODE_BLOOM)) {
            // either NODE_NETWORK or NODE_BLOOM bit is missing in node's services
            LOCK(cs_main);
            Misbehaving(pnode->GetId(), 100, "mnauth from a node with invalid services");
            return;
        }

        if (mnauth.proRegTxHash.IsNull()) {
            LOCK(cs_main);
            Misbehaving(pnode->GetId(), 100, "empty mnauth proRegTxHash");
            return;
        }

        if (!mnauth.sig.IsValid()) {
            LOCK(cs_main);
            Misbehaving(pnode->GetId(), 100, "invalid mnauth signature");
            return;
        }

        auto mnList = deterministicMNManager->GetListAtChainTip();
        auto dmn = mnList.GetMN(mnauth.proRegTxHash);
        if (!dmn) {
            LOCK(cs_main);
            // in case node was unlucky and not up to date, just let it be connected as a regular node, which gives it
            // a chance to get up-to-date and thus realize that it's not a MN anymore. We still give it a
            // low DoS score.
            Misbehaving(pnode->GetId(), 10, "missing mnauth masternode");
            return;
        }

        uint256 signHash;
        {
            LOCK(pnode->cs_mnauth);
            // See comment in PushMNAUTH (fInbound is negated here as we're on the other side of the connection)
            signHash = ::SerializeHash(std::make_tuple(dmn->pdmnState->pubKeyOperator, pnode->sentMNAuthChallenge, !pnode->fInbound));
        }

        if (!mnauth.sig.VerifyInsecure(dmn->pdmnState->pubKeyOperator.Get(), signHash)) {
            LOCK(cs_main);
            // Same as above, MN seems to not know its fate yet, so give it a chance to update. If this is a
            // malicious node (DoSing us), it'll get banned soon.
            Misbehaving(pnode->GetId(), 10, "mnauth signature verification failed");
            return;
        }

        if (!pnode->fInbound) {
            mmetaman.GetMetaInfo(mnauth.proRegTxHash)->SetLastOutboundSuccess(GetAdjustedTime());
            if (pnode->fMasternodeProbe) {
                LogPrint(BCLog::NET, "CMNAuth::ProcessMessage -- Masternode probe successful for %s, disconnecting. peer=%d\n",
                         mnauth.proRegTxHash.ToString(), pnode->GetId());
                pnode->fDisconnect = true;
                return;
            }
        }

        connman.ForEachNode([&](CNode* pnode2) {
            if (pnode->fDisconnect) {
                // we've already disconnected the new peer
                return;
            }

            if (pnode2->verifiedProRegTxHash == mnauth.proRegTxHash) {
                if (fMasternodeMode) {
                    auto deterministicOutbound = llmq::CLLMQUtils::DeterministicOutboundConnection(activeMasternodeInfo.proTxHash, mnauth.proRegTxHash);
                    LogPrint(BCLog::NET, "CMNAuth::ProcessMessage -- Masternode %s has already verified as peer %d, deterministicOutbound=%s. peer=%d\n",
                             mnauth.proRegTxHash.ToString(), pnode2->GetId(), deterministicOutbound.ToString(), pnode->GetId());
                    if (deterministicOutbound == activeMasternodeInfo.proTxHash) {
                        if (pnode2->fInbound) {
                            LogPrint(BCLog::NET, "CMNAuth::ProcessMessage -- dropping old inbound, peer=%d\n", pnode2->GetId());
                            pnode2->fDisconnect = true;
                        } else if (pnode->fInbound) {
                            LogPrint(BCLog::NET, "CMNAuth::ProcessMessage -- dropping new inbound, peer=%d\n", pnode->GetId());
                            pnode->fDisconnect = true;
                        }
                    } else {
                        if (!pnode2->fInbound) {
                            LogPrint(BCLog::NET, "CMNAuth::ProcessMessage -- dropping old outbound, peer=%d\n", pnode2->GetId());
                            pnode2->fDisconnect = true;
                        } else if (!pnode->fInbound) {
                            LogPrint(BCLog::NET, "CMNAuth::ProcessMessage -- dropping new outbound, peer=%d\n", pnode->GetId());
                            pnode->fDisconnect = true;
                        }
                    }
                } else {
                    LogPrint(BCLog::NET, "CMNAuth::ProcessMessage -- Masternode %s has already verified as peer %d, dropping new connection. peer=%d\n",
                            mnauth.proRegTxHash.ToString(), pnode2->GetId(), pnode->GetId());
                    pnode->fDisconnect = true;
                }
            }
        });

        if (pnode->fDisconnect) {
            return;
        }

        {
            LOCK(pnode->cs_mnauth);
            pnode->verifiedProRegTxHash = mnauth.proRegTxHash;
            pnode->verifiedPubKeyHash = dmn->pdmnState->pubKeyOperator.GetHash();
        }

        LogPrint(BCLog::NET, "CMNAuth::%s -- Valid MNAUTH for %s, peer=%d\n", __func__, mnauth.proRegTxHash.ToString(), pnode->GetId());
    }
}

void CMNAuth::NotifyMasternodeListChanged(bool undo, const CDeterministicMNList& oldMNList, const CDeterministicMNListDiff& diff, CConnman& connman)
{
    // we're only interested in updated/removed MNs. Added MNs are of no interest for us
    if (diff.updatedMNs.empty() && diff.removedMns.empty()) {
        return;
    }

    connman.ForEachNode([&](CNode* pnode) {
        LOCK(pnode->cs_mnauth);
        if (pnode->verifiedProRegTxHash.IsNull()) {
            return;
        }
        auto verifiedDmn = oldMNList.GetMN(pnode->verifiedProRegTxHash);
        if (!verifiedDmn) {
            return;
        }
        bool doRemove = false;
        if (diff.removedMns.count(verifiedDmn->GetInternalId())) {
            doRemove = true;
        } else {
            auto it = diff.updatedMNs.find(verifiedDmn->GetInternalId());
            if (it != diff.updatedMNs.end()) {
                if ((it->second.fields & CDeterministicMNStateDiff::Field_pubKeyOperator) && it->second.state.pubKeyOperator.GetHash() != pnode->verifiedPubKeyHash) {
                    doRemove = true;
                }
            }
        }

        if (doRemove) {
            LogPrint(BCLog::NET, "CMNAuth::NotifyMasternodeListChanged -- Disconnecting MN %s due to key changed/removed, peer=%d\n",
                     pnode->verifiedProRegTxHash.ToString(), pnode->GetId());
            pnode->fDisconnect = true;
        }
    });
}
<commit_msg>remove bloom requirement for mn<commit_after>// Copyright (c) 2019-2020 The Dash Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <evo/mnauth.h>

#include <evo/deterministicmns.h>
#include <llmq/quorums_utils.h>
#include <masternode/activemasternode.h>
#include <masternode/masternode-meta.h>
#include <masternode/masternode-sync.h>
#include <net.h>
#include <net_processing.h>
#include <netmessagemaker.h>
#include <validation.h>

#include <unordered_set>

void CMNAuth::PushMNAUTH(CNode* pnode, CConnman& connman)
{
    if (!fMasternodeMode || activeMasternodeInfo.proTxHash.IsNull()) {
        return;
    }

    uint256 signHash;
    {
        LOCK(pnode->cs_mnauth);
        if (pnode->receivedMNAuthChallenge.IsNull()) {
            return;
        }
        // We include fInbound in signHash to forbid interchanging of challenges by a man in the middle (MITM). This way
        // we protect ourselves against MITM in this form:
        //   node1 <- Eve -> node2
        // It does not protect against:
        //   node1 -> Eve -> node2
        // This is ok as we only use MNAUTH as a DoS protection and not for sensitive stuff
        signHash = ::SerializeHash(std::make_tuple(*activeMasternodeInfo.blsPubKeyOperator, pnode->receivedMNAuthChallenge, pnode->fInbound));
    }

    CMNAuth mnauth;
    mnauth.proRegTxHash = activeMasternodeInfo.proTxHash;
    mnauth.sig = activeMasternodeInfo.blsKeyOperator->Sign(signHash);

    LogPrint(BCLog::NET, "CMNAuth::%s -- Sending MNAUTH, peer=%d\n", __func__, pnode->GetId());

    connman.PushMessage(pnode, CNetMsgMaker(pnode->GetSendVersion()).Make(NetMsgType::MNAUTH, mnauth));
}

void CMNAuth::ProcessMessage(CNode* pnode, const std::string& strCommand, CDataStream& vRecv, CConnman& connman)
{
    if (!masternodeSync.IsBlockchainSynced()) {
        // we can't verify MNAUTH messages when we don't have the latest MN list
        return;
    }

    if (strCommand == NetMsgType::MNAUTH) {
        CMNAuth mnauth;
        vRecv >> mnauth;

        // only one MNAUTH allowed
        bool fAlreadyHaveMNAUTH = false;
        {
            LOCK(pnode->cs_mnauth);
            fAlreadyHaveMNAUTH = !pnode->verifiedProRegTxHash.IsNull();
        }
        if (fAlreadyHaveMNAUTH) {
            LOCK(cs_main);
            Misbehaving(pnode->GetId(), 100, "duplicate mnauth");
            return;
        }

        if ((~pnode->nServices) & NODE_NETWORK)) {
            // NODE_NETWORK bit is missing in node's services
            LOCK(cs_main);
            Misbehaving(pnode->GetId(), 100, "mnauth from a node with invalid services");
            return;
        }

        if (mnauth.proRegTxHash.IsNull()) {
            LOCK(cs_main);
            Misbehaving(pnode->GetId(), 100, "empty mnauth proRegTxHash");
            return;
        }

        if (!mnauth.sig.IsValid()) {
            LOCK(cs_main);
            Misbehaving(pnode->GetId(), 100, "invalid mnauth signature");
            return;
        }

        auto mnList = deterministicMNManager->GetListAtChainTip();
        auto dmn = mnList.GetMN(mnauth.proRegTxHash);
        if (!dmn) {
            LOCK(cs_main);
            // in case node was unlucky and not up to date, just let it be connected as a regular node, which gives it
            // a chance to get up-to-date and thus realize that it's not a MN anymore. We still give it a
            // low DoS score.
            Misbehaving(pnode->GetId(), 10, "missing mnauth masternode");
            return;
        }

        uint256 signHash;
        {
            LOCK(pnode->cs_mnauth);
            // See comment in PushMNAUTH (fInbound is negated here as we're on the other side of the connection)
            signHash = ::SerializeHash(std::make_tuple(dmn->pdmnState->pubKeyOperator, pnode->sentMNAuthChallenge, !pnode->fInbound));
        }

        if (!mnauth.sig.VerifyInsecure(dmn->pdmnState->pubKeyOperator.Get(), signHash)) {
            LOCK(cs_main);
            // Same as above, MN seems to not know its fate yet, so give it a chance to update. If this is a
            // malicious node (DoSing us), it'll get banned soon.
            Misbehaving(pnode->GetId(), 10, "mnauth signature verification failed");
            return;
        }

        if (!pnode->fInbound) {
            mmetaman.GetMetaInfo(mnauth.proRegTxHash)->SetLastOutboundSuccess(GetAdjustedTime());
            if (pnode->fMasternodeProbe) {
                LogPrint(BCLog::NET, "CMNAuth::ProcessMessage -- Masternode probe successful for %s, disconnecting. peer=%d\n",
                         mnauth.proRegTxHash.ToString(), pnode->GetId());
                pnode->fDisconnect = true;
                return;
            }
        }

        connman.ForEachNode([&](CNode* pnode2) {
            if (pnode->fDisconnect) {
                // we've already disconnected the new peer
                return;
            }

            if (pnode2->verifiedProRegTxHash == mnauth.proRegTxHash) {
                if (fMasternodeMode) {
                    auto deterministicOutbound = llmq::CLLMQUtils::DeterministicOutboundConnection(activeMasternodeInfo.proTxHash, mnauth.proRegTxHash);
                    LogPrint(BCLog::NET, "CMNAuth::ProcessMessage -- Masternode %s has already verified as peer %d, deterministicOutbound=%s. peer=%d\n",
                             mnauth.proRegTxHash.ToString(), pnode2->GetId(), deterministicOutbound.ToString(), pnode->GetId());
                    if (deterministicOutbound == activeMasternodeInfo.proTxHash) {
                        if (pnode2->fInbound) {
                            LogPrint(BCLog::NET, "CMNAuth::ProcessMessage -- dropping old inbound, peer=%d\n", pnode2->GetId());
                            pnode2->fDisconnect = true;
                        } else if (pnode->fInbound) {
                            LogPrint(BCLog::NET, "CMNAuth::ProcessMessage -- dropping new inbound, peer=%d\n", pnode->GetId());
                            pnode->fDisconnect = true;
                        }
                    } else {
                        if (!pnode2->fInbound) {
                            LogPrint(BCLog::NET, "CMNAuth::ProcessMessage -- dropping old outbound, peer=%d\n", pnode2->GetId());
                            pnode2->fDisconnect = true;
                        } else if (!pnode->fInbound) {
                            LogPrint(BCLog::NET, "CMNAuth::ProcessMessage -- dropping new outbound, peer=%d\n", pnode->GetId());
                            pnode->fDisconnect = true;
                        }
                    }
                } else {
                    LogPrint(BCLog::NET, "CMNAuth::ProcessMessage -- Masternode %s has already verified as peer %d, dropping new connection. peer=%d\n",
                            mnauth.proRegTxHash.ToString(), pnode2->GetId(), pnode->GetId());
                    pnode->fDisconnect = true;
                }
            }
        });

        if (pnode->fDisconnect) {
            return;
        }

        {
            LOCK(pnode->cs_mnauth);
            pnode->verifiedProRegTxHash = mnauth.proRegTxHash;
            pnode->verifiedPubKeyHash = dmn->pdmnState->pubKeyOperator.GetHash();
        }

        LogPrint(BCLog::NET, "CMNAuth::%s -- Valid MNAUTH for %s, peer=%d\n", __func__, mnauth.proRegTxHash.ToString(), pnode->GetId());
    }
}

void CMNAuth::NotifyMasternodeListChanged(bool undo, const CDeterministicMNList& oldMNList, const CDeterministicMNListDiff& diff, CConnman& connman)
{
    // we're only interested in updated/removed MNs. Added MNs are of no interest for us
    if (diff.updatedMNs.empty() && diff.removedMns.empty()) {
        return;
    }

    connman.ForEachNode([&](CNode* pnode) {
        LOCK(pnode->cs_mnauth);
        if (pnode->verifiedProRegTxHash.IsNull()) {
            return;
        }
        auto verifiedDmn = oldMNList.GetMN(pnode->verifiedProRegTxHash);
        if (!verifiedDmn) {
            return;
        }
        bool doRemove = false;
        if (diff.removedMns.count(verifiedDmn->GetInternalId())) {
            doRemove = true;
        } else {
            auto it = diff.updatedMNs.find(verifiedDmn->GetInternalId());
            if (it != diff.updatedMNs.end()) {
                if ((it->second.fields & CDeterministicMNStateDiff::Field_pubKeyOperator) && it->second.state.pubKeyOperator.GetHash() != pnode->verifiedPubKeyHash) {
                    doRemove = true;
                }
            }
        }

        if (doRemove) {
            LogPrint(BCLog::NET, "CMNAuth::NotifyMasternodeListChanged -- Disconnecting MN %s due to key changed/removed, peer=%d\n",
                     pnode->verifiedProRegTxHash.ToString(), pnode->GetId());
            pnode->fDisconnect = true;
        }
    });
}
<|endoftext|>


<commit_before>#include "facenet_tf.h"
#include <dirent.h>
#include <string.h>

Ptr<ml::KNearest>  k_nearest(ml::KNearest::create());
Ptr<ml::ANN_MLP> 	ann;
#define K_VALUE 1
#define N_LAYERS 3
FacenetClassifier::FacenetClassifier (string operation, string model_path, string svm_model_path, string labels_file_path) {
	this->operation = operation;
	this->model_path = model_path;
	this->svm_model_path = svm_model_path;
	this->labels_file_path = labels_file_path;
	ReadBinaryProto(tensorflow::Env::Default(), model_path.c_str(), &graph_def);
	tensorflow::SessionOptions options;
	tensorflow::NewSession (options, &session);
	session->Create (graph_def);

}

void FacenetClassifier::save_labels () {
	cout << "Saving Labels To " << labels_file_path << endl;
	labels_file.open (labels_file_path, fstream::out);

	for (Label label: class_labels) {
		labels_file << label.class_number << " " << label.class_name << endl;
	}
	labels_file.close ();
	cout << "Done Saving labels" << endl;
}

void FacenetClassifier::load_labels () {
	class_labels.clear ();
	labels_file.open (labels_file_path, fstream::in);
	int count = 0;
	while (true) {
		if (labels_file.eof())
			break;
		Label label;
		labels_file >> label.class_number >> label.class_name;
		class_labels.push_back (label);
		count++;
	}
	labels_file.close ();
	cout << class_labels.size() << endl;
}

void FacenetClassifier::parse_images_path (string directory_path, int depth) {
	cout << "Parsing Directory: " << directory_path << endl;
	DIR *dir;
	struct dirent *entry;
	static int class_count = -1;
	string class_name;
	string file_name, file_path;
	if ((dir = opendir (directory_path.c_str())) != NULL) {

		while ((entry = readdir (dir)) != NULL) {
			if (entry->d_type == DT_DIR && strcmp (entry->d_name, ".") !=0 && strcmp (entry->d_name, "..") !=0) {
				class_name = string (entry->d_name);
				class_count++;
				parse_images_path (directory_path + "/" + class_name, depth+1);
				Label label;
				label.class_number = class_count;
				label.class_name = class_name;
				class_labels.push_back(label);
			}
			else if (entry->d_type != DT_DIR) {
				file_name = string (entry->d_name);
				file_path = directory_path + "/" + file_name;
				Mat image = imread (file_path);
				if (image.empty() || image.rows !=160 || image.cols !=160)
					cout << "Ignoring Image " + file_path << endl;
				else {
					cout << file_path << ":" << class_count << endl;
					input_images.push_back (image);
					output_labels.push_back (class_count); //For Training
					mat_training_ints.push_back (class_count);
					input_files.push_back (file_path); //For Classification
				}
			}
		}
		closedir (dir);
	}
}

void FacenetClassifier::create_input_tensor () {
	cout << "Using " << input_images.size() << " images" << endl;

	Tensor input_tensor(DT_FLOAT, TensorShape({(int) input_images.size(), 160, 160, 3}));
	// get pointer to memory for that Tensor
	float *p = input_tensor.flat<float>().data();
	int i;

	for (i = 0; i < input_images.size(); i++) {
		// create a "fake" cv::Mat from it
		Mat camera_image(160 , 160, CV_32FC3, p + i*160*160*3);
		input_images[i].convertTo(camera_image, CV_32FC3);

	}
	cout << input_tensor.DebugString() << endl;
	this->input_tensor = input_tensor;
}

void FacenetClassifier::create_phase_tensor () {
	tensorflow::Tensor phase_tensor(tensorflow::DT_BOOL, tensorflow::TensorShape());
	phase_tensor.scalar<bool>()() = true;
	this->phase_tensor = phase_tensor;
}

void FacenetClassifier::run () {
	string input_layer = "input:0";
	string phase_train_layer = "phase_train:0";
	string output_layer = "embeddings:0";
	std::vector<tensorflow::Tensor> outputs;
	std::vector<std::pair<string, tensorflow::Tensor>> feed_dict = {
		  {input_layer, input_tensor},
		  {phase_train_layer, phase_tensor},
	};
	cout << "Input Tensor: " << input_tensor.DebugString() << endl;
	Status run_status = session->Run(feed_dict, {output_layer}, {} , &outputs);
	if (!run_status.ok()) {
		  LOG(ERROR) << "Running model failed: " << run_status << "\n";
		  return;
	}
	cout << "Output: " << outputs[0].DebugString() << endl;

	float *p = outputs[0].flat<float>().data();
	Mat output_mat;
	for (int i = 0; i < input_images.size(); i++) {
		Mat mat_row (cv::Size (128, 1), CV_32F, p + i*128, Mat::AUTO_STEP);
		Mat mat_row_float, mat_tensor_reshaped;
		mat_row.convertTo (mat_row_float, CV_32FC3);
		mat_tensor_reshaped = mat_row_float.reshape (0,1);
		mat_training_tensors.push_back (mat_tensor_reshaped);
		if (i == 0)
			output_mat = mat_row;
		else
			vconcat (output_mat, mat_row, output_mat);
	}
	this->output_mat = output_mat;

}

void FacenetClassifier::save_mlp () {

	ann = ann->create ();
	Mat train_data = mat_training_tensors;
	Mat train_labels = mat_training_ints;
	int nfeatures = mat_training_tensors.cols;
	cout << "Labels: " << train_labels << endl;
	int nclasses = class_labels.size();
	cout << "Classes: " << nclasses << endl;
	Mat_<int> layers(4,1);
	layers(0) = nfeatures;     // input
	layers(1) = nclasses * 8;  // hidden
	layers(2) = nclasses * 4;  // hidden
	layers(3) = nclasses;      // output, 1 pin per class.
	ann->setLayerSizes(layers);
	ann->setActivationFunction(ml::ANN_MLP::SIGMOID_SYM,0,0);
	ann->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER+TermCriteria::EPS, 300, 0.0001));
	ann->setTrainMethod(ml::ANN_MLP::BACKPROP, 0.0001);
	// ann requires "one-hot" encoding of class labels:
	Mat train_classes = Mat::zeros(train_data.rows, nclasses, CV_32FC1);

	for(int i=0; i<train_classes.rows; i++)
	{
		cout << i << ":" << train_labels.at<int>(i) << endl;
		train_classes.at<float>(i, train_labels.at<int>(i)) = 1.f;
	}

	cerr << train_data.size() << " " << train_classes.size() << endl;

	ann->train(train_data, ml::ROW_SAMPLE, train_classes);
	ann->save (svm_model_path);
}

void FacenetClassifier::load_mlp () {
	ann = ann->load (svm_model_path);
}

void FacenetClassifier::predict_mlp_labels () {
	float prediction;

	for (int i = 0; i < input_images.size(); i++) {
		Mat input_mat;
		Mat result;

		mat_training_tensors.row(i).convertTo (input_mat, CV_32F);
		prediction = ann->predict (input_mat, result);
		cout << input_files[i] << " " << prediction << " " << result.size() << " " << result.at<float>(0, prediction) << endl;

		/*Point maxP;
		minMaxLoc(result, 0,0,0,&maxP);
		prediction = maxP.x;
		cout << input_files[i] <<" " << prediction << endl;*/
	}
}

void FacenetClassifier::save_svm () {
	svm = SVM::create();
	svm->setKernel(SVM::LINEAR);
	svm->train(output_mat, ROW_SAMPLE, output_labels);
	svm->save(svm_model_path);
	cout << "Training Complete" << endl;
}

void FacenetClassifier::save_knn () {
	Mat mat_training_floats;
	mat_training_ints.convertTo(mat_training_floats, CV_32FC3);

	FileStorage training_labels_file ("labels.xml", FileStorage::WRITE);
	training_labels_file << "classifications" << mat_training_floats;
	training_labels_file.release ();

	FileStorage training_tensors_file (svm_model_path, FileStorage::WRITE);
	training_tensors_file << "images" << mat_training_tensors;
	training_tensors_file.release ();
}

void FacenetClassifier::load_svm () {
	svm = Algorithm::load<SVM>(svm_model_path);
	cout << "SVM Model Loaded" << endl;
}

void FacenetClassifier::predict_labels () {
	Mat svm_response;
	svm->predict (output_mat, svm_response);
	//svm->predict (output_mat, svm_response);
	cout << "SVM Response: " << svm_response << endl;
  for (int i = 0 ; i < svm_response.rows; i++) {

  	//int class_number = (int) svm_response.at<float>(i,0);
  	cout << input_files[i] << ": " << svm_response.at<float>(i) << endl;
    //cout << input_files[i] << ": " << class_labels[class_number-1].class_name << endl;
  }
}

void FacenetClassifier::load_knn () {
	FileStorage labels_knn("labels.xml", FileStorage::READ);
	labels_knn["classifications"] >> mat_training_ints;
	labels_knn.release();
	cout << "KNN Labels: " << mat_training_ints.size() << endl;
	cout << "Reading " << model_path << endl;
	FileStorage tensors (svm_model_path, FileStorage::READ);
	tensors["images"] >> mat_training_tensors;
	tensors.release();
	cout << "KNN Tensors: " << mat_training_tensors.size() << endl;
	k_nearest->setDefaultK (1);
	cout << "Train: " << k_nearest->train (mat_training_tensors, ml::ROW_SAMPLE, mat_training_ints) << endl;
}

void FacenetClassifier::predict_knn_labels () {
	Mat current_class(0, 0, CV_32F);
	Mat current_response(0, 0, CV_32F);
	Mat current_distance (0, 0, CV_32F);
	float current_class_float, response, distance;
	float prediction;

	for (int i = 0; i < input_images.size(); i++) {
		Mat input_mat, input_mat_flattened;
		mat_training_tensors.row(i).convertTo (input_mat, CV_32FC3);
		input_mat_flattened = input_mat.reshape (0,1);

		prediction = k_nearest->findNearest (input_mat_flattened, K_VALUE, current_class, current_response, current_distance);
		current_class_float = (float) current_class.at<float>(0,0);
		response = (float) current_response.at<float>(0,0);
		distance = (float) current_distance.at<float>(0,0);
		cout << mat_training_tensors.row(i).size() << " " << prediction << " " << input_files[i] << ": " << current_class_float << " " << distance << endl;
	}

}
<commit_msg>Bug Fix To ANN MLP Prediction Function<commit_after>#include "facenet_tf.h"
#include <dirent.h>
#include <string.h>

Ptr<ml::KNearest>  k_nearest(ml::KNearest::create());
Ptr<ml::ANN_MLP> 	ann;
#define K_VALUE 1
#define N_LAYERS 3
FacenetClassifier::FacenetClassifier (string operation, string model_path, string svm_model_path, string labels_file_path) {
	this->operation = operation;
	this->model_path = model_path;
	this->svm_model_path = svm_model_path;
	this->labels_file_path = labels_file_path;
	ReadBinaryProto(tensorflow::Env::Default(), model_path.c_str(), &graph_def);
	tensorflow::SessionOptions options;
	tensorflow::NewSession (options, &session);
	session->Create (graph_def);

}

void FacenetClassifier::save_labels () {
	cout << "Saving Labels To " << labels_file_path << endl;
	labels_file.open (labels_file_path, fstream::out);

	for (Label label: class_labels) {
		labels_file << label.class_number << " " << label.class_name << endl;
	}
	labels_file.close ();
	cout << "Done Saving labels" << endl;
}

void FacenetClassifier::load_labels () {
	class_labels.clear ();
	labels_file.open (labels_file_path, fstream::in);
	int count = 0;
	while (true) {
		if (labels_file.eof())
			break;
		Label label;
		labels_file >> label.class_number >> label.class_name;
		class_labels.push_back (label);
		count++;
	}
	labels_file.close ();
	cout << class_labels.size() << endl;
}

void FacenetClassifier::parse_images_path (string directory_path, int depth) {
	cout << "Parsing Directory: " << directory_path << endl;
	DIR *dir;
	struct dirent *entry;
	static int class_count = -1;
	string class_name;
	string file_name, file_path;
	if ((dir = opendir (directory_path.c_str())) != NULL) {

		while ((entry = readdir (dir)) != NULL) {
			if (entry->d_type == DT_DIR && strcmp (entry->d_name, ".") !=0 && strcmp (entry->d_name, "..") !=0) {
				class_name = string (entry->d_name);
				class_count++;
				parse_images_path (directory_path + "/" + class_name, depth+1);
				Label label;
				label.class_number = class_count;
				label.class_name = class_name;
				class_labels.push_back(label);
			}
			else if (entry->d_type != DT_DIR) {
				file_name = string (entry->d_name);
				file_path = directory_path + "/" + file_name;
				Mat image = imread (file_path);
				if (image.empty() || image.rows !=160 || image.cols !=160)
					cout << "Ignoring Image " + file_path << endl;
				else {
					cout << file_path << ":" << class_count << endl;
					input_images.push_back (image);
					output_labels.push_back (class_count); //For Training
					mat_training_ints.push_back (class_count);
					input_files.push_back (file_path); //For Classification
				}
			}
		}
		closedir (dir);
	}
}

void FacenetClassifier::create_input_tensor () {
	cout << "Using " << input_images.size() << " images" << endl;

	Tensor input_tensor(DT_FLOAT, TensorShape({(int) input_images.size(), 160, 160, 3}));
	// get pointer to memory for that Tensor
	float *p = input_tensor.flat<float>().data();
	int i;

	for (i = 0; i < input_images.size(); i++) {
		// create a "fake" cv::Mat from it
		Mat camera_image(160 , 160, CV_32FC3, p + i*160*160*3);
		input_images[i].convertTo(camera_image, CV_32FC3);

	}
	cout << input_tensor.DebugString() << endl;
	this->input_tensor = input_tensor;
}

void FacenetClassifier::create_phase_tensor () {
	tensorflow::Tensor phase_tensor(tensorflow::DT_BOOL, tensorflow::TensorShape());
	phase_tensor.scalar<bool>()() = true;
	this->phase_tensor = phase_tensor;
}

void FacenetClassifier::run () {
	string input_layer = "input:0";
	string phase_train_layer = "phase_train:0";
	string output_layer = "embeddings:0";
	std::vector<tensorflow::Tensor> outputs;
	std::vector<std::pair<string, tensorflow::Tensor>> feed_dict = {
		  {input_layer, input_tensor},
		  {phase_train_layer, phase_tensor},
	};
	cout << "Input Tensor: " << input_tensor.DebugString() << endl;
	Status run_status = session->Run(feed_dict, {output_layer}, {} , &outputs);
	if (!run_status.ok()) {
		  LOG(ERROR) << "Running model failed: " << run_status << "\n";
		  return;
	}
	cout << "Output: " << outputs[0].DebugString() << endl;

	float *p = outputs[0].flat<float>().data();
	Mat output_mat;
	for (int i = 0; i < input_images.size(); i++) {
		Mat mat_row (cv::Size (128, 1), CV_32F, p + i*128, Mat::AUTO_STEP);
		Mat mat_row_float, mat_tensor_reshaped;
		mat_row.convertTo (mat_row_float, CV_32FC3);
		mat_tensor_reshaped = mat_row_float.reshape (0,1);
		mat_training_tensors.push_back (mat_tensor_reshaped);
		if (i == 0)
			output_mat = mat_row;
		else
			vconcat (output_mat, mat_row, output_mat);
	}
	this->output_mat = output_mat;

}

void FacenetClassifier::save_mlp () {

	ann = ann->create ();
	Mat train_data = mat_training_tensors;
	Mat train_labels = mat_training_ints;
	int nfeatures = mat_training_tensors.cols;
	cout << "Labels: " << train_labels << endl;
	int nclasses = class_labels.size();
	cout << "Classes: " << nclasses << endl;
	Mat_<int> layers(4,1);
	layers(0) = nfeatures;     // input
	layers(1) = nclasses * 8;  // hidden
	layers(2) = nclasses * 4;  // hidden
	layers(3) = nclasses;      // output, 1 pin per class.
	ann->setLayerSizes(layers);
	ann->setActivationFunction(ml::ANN_MLP::SIGMOID_SYM,0,0);
	ann->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER+TermCriteria::EPS, 300, 0.0001));
	ann->setTrainMethod(ml::ANN_MLP::BACKPROP, 0.0001);
	// ann requires "one-hot" encoding of class labels:
	Mat train_classes = Mat::zeros(train_data.rows, nclasses, CV_32FC1);

	for(int i=0; i<train_classes.rows; i++)
	{
		cout << i << ":" << train_labels.at<int>(i) << endl;
		train_classes.at<float>(i, train_labels.at<int>(i)) = 1.f;
	}

	cerr << train_data.size() << " " << train_classes.size() << endl;

	ann->train(train_data, ml::ROW_SAMPLE, train_classes);
	ann->save (svm_model_path);
}

void FacenetClassifier::load_mlp () {
	ann = ann->load (svm_model_path);
}

void FacenetClassifier::predict_mlp_labels () {
	float prediction;

	for (int i = 0; i < input_images.size(); i++) {
		Mat input_mat;
		Mat result;

		output_mat.row(i).convertTo (input_mat, CV_32F);
		prediction = ann->predict (input_mat, result);
		cout << input_files[i] << " " << prediction << " " << result.size() << " " << result.at<float>(0, prediction) << endl;

		/*Point maxP;
		minMaxLoc(result, 0,0,0,&maxP);
		prediction = maxP.x;
		cout << input_files[i] <<" " << prediction << endl;*/
	}
}

void FacenetClassifier::save_svm () {
	svm = SVM::create();
	svm->setKernel(SVM::LINEAR);
	svm->train(output_mat, ROW_SAMPLE, output_labels);
	svm->save(svm_model_path);
	cout << "Training Complete" << endl;
}

void FacenetClassifier::save_knn () {
	Mat mat_training_floats;
	mat_training_ints.convertTo(mat_training_floats, CV_32FC3);

	FileStorage training_labels_file ("labels.xml", FileStorage::WRITE);
	training_labels_file << "classifications" << mat_training_floats;
	training_labels_file.release ();

	FileStorage training_tensors_file (svm_model_path, FileStorage::WRITE);
	training_tensors_file << "images" << mat_training_tensors;
	training_tensors_file.release ();
}

void FacenetClassifier::load_svm () {
	svm = Algorithm::load<SVM>(svm_model_path);
	cout << "SVM Model Loaded" << endl;
}

void FacenetClassifier::predict_labels () {
	Mat svm_response;
	svm->predict (output_mat, svm_response);
	//svm->predict (output_mat, svm_response);
	cout << "SVM Response: " << svm_response << endl;
  for (int i = 0 ; i < svm_response.rows; i++) {

  	//int class_number = (int) svm_response.at<float>(i,0);
  	cout << input_files[i] << ": " << svm_response.at<float>(i) << endl;
    //cout << input_files[i] << ": " << class_labels[class_number-1].class_name << endl;
  }
}

void FacenetClassifier::load_knn () {
	FileStorage labels_knn("labels.xml", FileStorage::READ);
	labels_knn["classifications"] >> mat_training_ints;
	labels_knn.release();
	cout << "KNN Labels: " << mat_training_ints.size() << endl;
	cout << "Reading " << model_path << endl;
	FileStorage tensors (svm_model_path, FileStorage::READ);
	tensors["images"] >> mat_training_tensors;
	tensors.release();
	cout << "KNN Tensors: " << mat_training_tensors.size() << endl;
	k_nearest->setDefaultK (1);
	cout << "Train: " << k_nearest->train (mat_training_tensors, ml::ROW_SAMPLE, mat_training_ints) << endl;
}

void FacenetClassifier::predict_knn_labels () {
	Mat current_class(0, 0, CV_32F);
	Mat current_response(0, 0, CV_32F);
	Mat current_distance (0, 0, CV_32F);
	float current_class_float, response, distance;
	float prediction;

	for (int i = 0; i < input_images.size(); i++) {
		Mat input_mat, input_mat_flattened;
		mat_training_tensors.row(i).convertTo (input_mat, CV_32FC3);
		input_mat_flattened = input_mat.reshape (0,1);

		prediction = k_nearest->findNearest (input_mat_flattened, K_VALUE, current_class, current_response, current_distance);
		current_class_float = (float) current_class.at<float>(0,0);
		response = (float) current_response.at<float>(0,0);
		distance = (float) current_distance.at<float>(0,0);
		cout << mat_training_tensors.row(i).size() << " " << prediction << " " << input_files[i] << ": " << current_class_float << " " << distance << endl;
	}

}
<|endoftext|>