<commit_before>/*
 *  This file is part of Poedit (https://poedit.net)
 *
 *  Copyright (C) 2016 Vaclav Slavik
 *
 *  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 "custom_buttons.h"

#include "colorscheme.h"
#include "hidpi.h"
#include "str_helpers.h"
#include "utility.h"

#ifdef __WXMSW__
#include <wx/dc.h>
#include <wx/graphics.h>
#include <wx/scopedptr.h>
#include <wx/msw/wrapgdip.h>
#include <wx/msw/uxtheme.h>
#endif

#ifdef __WXOSX__

#import <AppKit/AppKit.h>
#include "StyleKit.h"

// ---------------------------------------------------------------------
// SwitchButton
// ---------------------------------------------------------------------

@interface POSwitchButton : NSButton

@property NSColor* onColor;
@property NSColor* labelOffColor;
@property SwitchButton *parent;
@property double animationPosition;

@end

@implementation POSwitchButton

- (id)initWithLabel:(NSString*)label
{
    self = [super init];
    if (self)
    {
        self.title = label;
        self.bezelStyle = NSSmallSquareBezelStyle;
        self.buttonType = NSOnOffButton;
        self.font = [NSFont boldSystemFontOfSize:[NSFont smallSystemFontSize]];
        self.animationPosition = 0.0;
        self.onColor = [NSColor colorWithCalibratedRed:0.302 green:0.847 blue:0.396 alpha:1.0];
        self.labelOffColor = [NSColor colorWithCalibratedRed:0 green:0 blue:0 alpha:1.0];
    }
    return self;
}

- (void)sizeToFit
{
    [super sizeToFit];
    NSSize size = self.frame.size;
    size.width += 32;
    [self setFrameSize:size];
}

- (void)drawRect:(NSRect)dirtyRect
{
    #pragma unused(dirtyRect)
    [StyleKit drawSwitchButtonWithFrame:self.bounds
                                onColor:self.onColor
                          labelOffColor:self.labelOffColor
                                  label:self.title
                         togglePosition:self.animationPosition];
}

- (void)setState:(NSInteger)state
{
    [super setState:state];
    self.animationPosition = (state == NSOnState) ? 1.0 : 0.0;
}

- (void)controlAction:(id)sender
{
    #pragma unused(sender)
    self.animationPosition = (self.state == NSOnState) ? 1.0 : 0.0;
    _parent->SendToggleEvent();
}

@end

class SwitchButton::impl
{
public:
    impl(SwitchButton *parent, const wxString& label)
    {
        m_view = [[POSwitchButton alloc] initWithLabel:str::to_NS(label)];
        m_view.parent = parent;
    }

    NSView *View() const { return m_view; }

    void SetColors(const wxColour& on, const wxColour& offLabel)
    {
        m_view.onColor = on.OSXGetNSColor();
        m_view.labelOffColor = offLabel.OSXGetNSColor();
    }

    void SetValue(bool value)
    {
        m_view.state = value ? NSOnState : NSOffState;
    }

    bool GetValue() const
    {
        return m_view.state == NSOnState;
    }

private:
    POSwitchButton *m_view;
};

SwitchButton::SwitchButton(wxWindow *parent, wxWindowID winid, const wxString& label)
{
    m_impl.reset(new impl(this, label));

    wxNativeWindow::Create(parent, winid, m_impl->View());
}

SwitchButton::~SwitchButton()
{
}

void SwitchButton::SetColors(const wxColour& on, const wxColour& offLabel)
{
    m_impl->SetColors(on, offLabel);
}

void SwitchButton::SetValue(bool value)
{
    m_impl->SetValue(value);
}

bool SwitchButton::GetValue() const
{
    return m_impl->GetValue();
}

void SwitchButton::SendToggleEvent()
{
    wxCommandEvent event(wxEVT_TOGGLEBUTTON, m_windowId);
    event.SetInt(GetValue());
    event.SetEventObject(this);
    ProcessWindowEvent(event);
}

#else // !__WXOSX__

SwitchButton::SwitchButton(wxWindow *parent, wxWindowID winid, const wxString& label)
{
    long style = wxBU_EXACTFIT;
#ifdef __WXMSW__
    style |= wxNO_BORDER;
#endif

    wxToggleButton::Create(parent, winid, label, wxDefaultPosition, wxDefaultSize, style);

#ifdef __WXMSW__
    SetFont(GetFont().Bold());
    SetBackgroundColour(parent->GetBackgroundColour());
    MakeOwnerDrawn();
    Bind(wxEVT_LEFT_DOWN, &SwitchButton::OnMouseClick, this);
#endif
}

void SwitchButton::SetColors(const wxColour& on, const wxColour& offLabel)
{
    (void)on;
    (void)offLabel;
#ifdef __WXMSW__
    m_clrOn = on;
    m_clrOffLabel = offLabel;
#endif
}

#ifdef __WXMSW__

void SwitchButton::OnMouseClick(wxMouseEvent& e)
{
    // normal click handling moves focus to the switch (which is a button
    // underneath), which we'd rather not do
    SetValue(!GetValue());
}

wxSize SwitchButton::DoGetBestSize() const
{
    auto size = wxToggleButton::DoGetBestSize();
    size.x += PX(38);
    size.y = PX(20);
    return size;
}

bool SwitchButton::MSWOnDraw(WXDRAWITEMSTRUCT *wxdis)
{
    LPDRAWITEMSTRUCT lpDIS = (LPDRAWITEMSTRUCT) wxdis;
    HDC hdc = lpDIS->hDC;

    UINT state = lpDIS->itemState;
    if (GetNormalState() == State_Pressed)
        state |= ODS_SELECTED;
    const bool toggled = state & ODS_SELECTED;

    wxRect rect(lpDIS->rcItem.left, lpDIS->rcItem.top, lpDIS->rcItem.right - lpDIS->rcItem.left, lpDIS->rcItem.bottom - lpDIS->rcItem.top);

    // FIXME: This is a mess, but cleaner solution needs support in wx
    static wxScopedPtr<wxGraphicsContext> gcdummy(wxGraphicsContext::Create(this));
    gcdummy.reset();

    wxScopedPtr<wxGraphicsContext> gc(wxGraphicsContext::CreateFromNative(new Graphics(hdc)));
    gc->EnableOffset(false);

    if (toggled)
    {
        gc->SetBrush(m_clrOn);
        gc->SetPen(wxPen(m_clrOn.ChangeLightness(95), PX(2)));
    }
    else
    {
        gc->SetBrush(GetBackgroundColour());
        gc->SetPen(wxPen(m_clrOffLabel, PX(2)));
    }

    wxRect switchRect(rect.GetRight() - PX(44), 0, PX(44), wxMin(PX(20), rect.GetHeight()));
    switchRect.CenterIn(rect, wxVERTICAL);
    switchRect.Deflate(PX(2));

    double radius = switchRect.height / 2.0;
    gc->DrawRoundedRectangle(switchRect.x, switchRect.y, switchRect.width, switchRect.height, radius);

    if (toggled)
    {
        gc->SetPen(GetBackgroundColour());
        gc->SetBrush(GetBackgroundColour());
    }
    else
    {
        gc->SetPen(wxPen(m_clrOffLabel, PX(1)));
        gc->SetBrush(m_clrOffLabel.ChangeLightness(105));
    }

    double position = toggled ? 1.0 : 0.0;
    wxRect dotRect(switchRect);
    dotRect.Deflate(PX(4));
    dotRect.SetLeft(dotRect.GetLeft() + position * (dotRect.GetWidth() - dotRect.GetHeight()));
    dotRect.SetWidth(dotRect.GetHeight());
    gc->DrawEllipse(dotRect.x, dotRect.y, dotRect.width, dotRect.height);

    gc->SetFont(GetFont(), toggled ? m_clrOn : m_clrOffLabel);
    double textw, texth, descent;
    gc->GetTextExtent(GetLabel(), &textw, &texth, &descent);
    texth += descent;
    wxCoord textpos = switchRect.y + (switchRect.height - texth) / 2 + PX(0.5);
    gc->DrawText(GetLabel(), rect.x, textpos);

    // draw the focus rectangle if we need it
    if ((state & ODS_FOCUS) && !(state & ODS_NOFOCUSRECT))
    {
        RECT r = {rect.x, textpos, rect.x + (int)textw, textpos + (int)texth};
        DrawFocusRect(hdc, &r);
    }

    return true;
}

#endif // __WXMSW__

#endif // !__WXOSX__

// ---------------------------------------------------------------------
// TranslucentButton
// ---------------------------------------------------------------------

#ifdef __WXOSX__

@interface POTranslucentButton : NSButton

@property TranslucentButton *parent;

@end

@implementation POTranslucentButton

- (id)initWithLabel:(NSString*)label
{
    self = [super init];
    if (self)
    {
        self.title = label;
        self.bezelStyle = NSSmallSquareBezelStyle;
        self.buttonType = NSMomentaryPushInButton;
        self.font = [NSFont systemFontOfSize:[NSFont smallSystemFontSize]];
    }
    return self;
}

- (void)sizeToFit
{
    NSSize size = self.attributedTitle.size;
    size.width += 28;
    size.height = 24;
    [self setFrameSize:size];
}

- (void)drawRect:(NSRect)dirtyRect
{
    #pragma unused(dirtyRect)
    [StyleKit drawTranslucentButtonWithFrame:self.bounds label:[self title] pressed:[self isHighlighted]];
}

- (void)controlAction:(id)sender
{
    #pragma unused(sender)
    wxCommandEvent event(wxEVT_BUTTON, _parent->GetId());
    event.SetEventObject(_parent);
    _parent->ProcessWindowEvent(event);
}

@end

TranslucentButton::TranslucentButton(wxWindow *parent, wxWindowID winid, const wxString& label)
{
    POTranslucentButton *view = [[POTranslucentButton alloc] initWithLabel:str::to_NS(label)];
    view.parent = this;
    wxNativeWindow::Create(parent, winid, view);
}

#else // !__WXOSX__

TranslucentButton::TranslucentButton(wxWindow *parent, wxWindowID winid, const wxString& label)
{
    wxButton::Create(parent, winid, label);
#ifdef __WXMSW__
    if (IsWindows10OrGreater())
        SetBackgroundColour(ColorScheme::GetBlendedOn(::Color::TranslucentButton, parent));
#endif
}

#endif // !__WXOSX__
<commit_msg>Send wxEVT_TOGGLEBUTTON  from SwitchButton on MSW<commit_after>/*
 *  This file is part of Poedit (https://poedit.net)
 *
 *  Copyright (C) 2016 Vaclav Slavik
 *
 *  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 "custom_buttons.h"

#include "colorscheme.h"
#include "hidpi.h"
#include "str_helpers.h"
#include "utility.h"

#ifdef __WXMSW__
#include <wx/dc.h>
#include <wx/graphics.h>
#include <wx/scopedptr.h>
#include <wx/msw/wrapgdip.h>
#include <wx/msw/uxtheme.h>
#endif

#ifdef __WXOSX__

#import <AppKit/AppKit.h>
#include "StyleKit.h"

// ---------------------------------------------------------------------
// SwitchButton
// ---------------------------------------------------------------------

@interface POSwitchButton : NSButton

@property NSColor* onColor;
@property NSColor* labelOffColor;
@property SwitchButton *parent;
@property double animationPosition;

@end

@implementation POSwitchButton

- (id)initWithLabel:(NSString*)label
{
    self = [super init];
    if (self)
    {
        self.title = label;
        self.bezelStyle = NSSmallSquareBezelStyle;
        self.buttonType = NSOnOffButton;
        self.font = [NSFont boldSystemFontOfSize:[NSFont smallSystemFontSize]];
        self.animationPosition = 0.0;
        self.onColor = [NSColor colorWithCalibratedRed:0.302 green:0.847 blue:0.396 alpha:1.0];
        self.labelOffColor = [NSColor colorWithCalibratedRed:0 green:0 blue:0 alpha:1.0];
    }
    return self;
}

- (void)sizeToFit
{
    [super sizeToFit];
    NSSize size = self.frame.size;
    size.width += 32;
    [self setFrameSize:size];
}

- (void)drawRect:(NSRect)dirtyRect
{
    #pragma unused(dirtyRect)
    [StyleKit drawSwitchButtonWithFrame:self.bounds
                                onColor:self.onColor
                          labelOffColor:self.labelOffColor
                                  label:self.title
                         togglePosition:self.animationPosition];
}

- (void)setState:(NSInteger)state
{
    [super setState:state];
    self.animationPosition = (state == NSOnState) ? 1.0 : 0.0;
}

- (void)controlAction:(id)sender
{
    #pragma unused(sender)
    self.animationPosition = (self.state == NSOnState) ? 1.0 : 0.0;
    _parent->SendToggleEvent();
}

@end

class SwitchButton::impl
{
public:
    impl(SwitchButton *parent, const wxString& label)
    {
        m_view = [[POSwitchButton alloc] initWithLabel:str::to_NS(label)];
        m_view.parent = parent;
    }

    NSView *View() const { return m_view; }

    void SetColors(const wxColour& on, const wxColour& offLabel)
    {
        m_view.onColor = on.OSXGetNSColor();
        m_view.labelOffColor = offLabel.OSXGetNSColor();
    }

    void SetValue(bool value)
    {
        m_view.state = value ? NSOnState : NSOffState;
    }

    bool GetValue() const
    {
        return m_view.state == NSOnState;
    }

private:
    POSwitchButton *m_view;
};

SwitchButton::SwitchButton(wxWindow *parent, wxWindowID winid, const wxString& label)
{
    m_impl.reset(new impl(this, label));

    wxNativeWindow::Create(parent, winid, m_impl->View());
}

SwitchButton::~SwitchButton()
{
}

void SwitchButton::SetColors(const wxColour& on, const wxColour& offLabel)
{
    m_impl->SetColors(on, offLabel);
}

void SwitchButton::SetValue(bool value)
{
    m_impl->SetValue(value);
}

bool SwitchButton::GetValue() const
{
    return m_impl->GetValue();
}

void SwitchButton::SendToggleEvent()
{
    wxCommandEvent event(wxEVT_TOGGLEBUTTON, m_windowId);
    event.SetInt(GetValue());
    event.SetEventObject(this);
    ProcessWindowEvent(event);
}

#else // !__WXOSX__

SwitchButton::SwitchButton(wxWindow *parent, wxWindowID winid, const wxString& label)
{
    long style = wxBU_EXACTFIT;
#ifdef __WXMSW__
    style |= wxNO_BORDER;
#endif

    wxToggleButton::Create(parent, winid, label, wxDefaultPosition, wxDefaultSize, style);

#ifdef __WXMSW__
    SetFont(GetFont().Bold());
    SetBackgroundColour(parent->GetBackgroundColour());
    MakeOwnerDrawn();
    Bind(wxEVT_LEFT_DOWN, &SwitchButton::OnMouseClick, this);
#endif
}

void SwitchButton::SetColors(const wxColour& on, const wxColour& offLabel)
{
    (void)on;
    (void)offLabel;
#ifdef __WXMSW__
    m_clrOn = on;
    m_clrOffLabel = offLabel;
#endif
}

#ifdef __WXMSW__

void SwitchButton::OnMouseClick(wxMouseEvent& e)
{
    // normal click handling moves focus to the switch (which is a button
    // underneath), which we'd rather not do
    SetValue(!GetValue());

    // we need to send the event, because SetValue() doesn't
    wxCommandEvent event(wxEVT_TOGGLEBUTTON, GetId());
    event.SetInt(GetValue());
    event.SetEventObject(this);
    ProcessCommand(event);
}

wxSize SwitchButton::DoGetBestSize() const
{
    auto size = wxToggleButton::DoGetBestSize();
    size.x += PX(38);
    size.y = PX(20);
    return size;
}

bool SwitchButton::MSWOnDraw(WXDRAWITEMSTRUCT *wxdis)
{
    LPDRAWITEMSTRUCT lpDIS = (LPDRAWITEMSTRUCT) wxdis;
    HDC hdc = lpDIS->hDC;

    UINT state = lpDIS->itemState;
    if (GetNormalState() == State_Pressed)
        state |= ODS_SELECTED;
    const bool toggled = state & ODS_SELECTED;

    wxRect rect(lpDIS->rcItem.left, lpDIS->rcItem.top, lpDIS->rcItem.right - lpDIS->rcItem.left, lpDIS->rcItem.bottom - lpDIS->rcItem.top);

    // FIXME: This is a mess, but cleaner solution needs support in wx
    static wxScopedPtr<wxGraphicsContext> gcdummy(wxGraphicsContext::Create(this));
    gcdummy.reset();

    wxScopedPtr<wxGraphicsContext> gc(wxGraphicsContext::CreateFromNative(new Graphics(hdc)));
    gc->EnableOffset(false);

    if (toggled)
    {
        gc->SetBrush(m_clrOn);
        gc->SetPen(wxPen(m_clrOn.ChangeLightness(95), PX(2)));
    }
    else
    {
        gc->SetBrush(GetBackgroundColour());
        gc->SetPen(wxPen(m_clrOffLabel, PX(2)));
    }

    wxRect switchRect(rect.GetRight() - PX(44), 0, PX(44), wxMin(PX(20), rect.GetHeight()));
    switchRect.CenterIn(rect, wxVERTICAL);
    switchRect.Deflate(PX(2));

    double radius = switchRect.height / 2.0;
    gc->DrawRoundedRectangle(switchRect.x, switchRect.y, switchRect.width, switchRect.height, radius);

    if (toggled)
    {
        gc->SetPen(GetBackgroundColour());
        gc->SetBrush(GetBackgroundColour());
    }
    else
    {
        gc->SetPen(wxPen(m_clrOffLabel, PX(1)));
        gc->SetBrush(m_clrOffLabel.ChangeLightness(105));
    }

    double position = toggled ? 1.0 : 0.0;
    wxRect dotRect(switchRect);
    dotRect.Deflate(PX(4));
    dotRect.SetLeft(dotRect.GetLeft() + position * (dotRect.GetWidth() - dotRect.GetHeight()));
    dotRect.SetWidth(dotRect.GetHeight());
    gc->DrawEllipse(dotRect.x, dotRect.y, dotRect.width, dotRect.height);

    gc->SetFont(GetFont(), toggled ? m_clrOn : m_clrOffLabel);
    double textw, texth, descent;
    gc->GetTextExtent(GetLabel(), &textw, &texth, &descent);
    texth += descent;
    wxCoord textpos = switchRect.y + (switchRect.height - texth) / 2 + PX(0.5);
    gc->DrawText(GetLabel(), rect.x, textpos);

    // draw the focus rectangle if we need it
    if ((state & ODS_FOCUS) && !(state & ODS_NOFOCUSRECT))
    {
        RECT r = {rect.x, textpos, rect.x + (int)textw, textpos + (int)texth};
        DrawFocusRect(hdc, &r);
    }

    return true;
}

#endif // __WXMSW__

#endif // !__WXOSX__

// ---------------------------------------------------------------------
// TranslucentButton
// ---------------------------------------------------------------------

#ifdef __WXOSX__

@interface POTranslucentButton : NSButton

@property TranslucentButton *parent;

@end

@implementation POTranslucentButton

- (id)initWithLabel:(NSString*)label
{
    self = [super init];
    if (self)
    {
        self.title = label;
        self.bezelStyle = NSSmallSquareBezelStyle;
        self.buttonType = NSMomentaryPushInButton;
        self.font = [NSFont systemFontOfSize:[NSFont smallSystemFontSize]];
    }
    return self;
}

- (void)sizeToFit
{
    NSSize size = self.attributedTitle.size;
    size.width += 28;
    size.height = 24;
    [self setFrameSize:size];
}

- (void)drawRect:(NSRect)dirtyRect
{
    #pragma unused(dirtyRect)
    [StyleKit drawTranslucentButtonWithFrame:self.bounds label:[self title] pressed:[self isHighlighted]];
}

- (void)controlAction:(id)sender
{
    #pragma unused(sender)
    wxCommandEvent event(wxEVT_BUTTON, _parent->GetId());
    event.SetEventObject(_parent);
    _parent->ProcessWindowEvent(event);
}

@end

TranslucentButton::TranslucentButton(wxWindow *parent, wxWindowID winid, const wxString& label)
{
    POTranslucentButton *view = [[POTranslucentButton alloc] initWithLabel:str::to_NS(label)];
    view.parent = this;
    wxNativeWindow::Create(parent, winid, view);
}

#else // !__WXOSX__

TranslucentButton::TranslucentButton(wxWindow *parent, wxWindowID winid, const wxString& label)
{
    wxButton::Create(parent, winid, label);
#ifdef __WXMSW__
    if (IsWindows10OrGreater())
        SetBackgroundColour(ColorScheme::GetBlendedOn(::Color::TranslucentButton, parent));
#endif
}

#endif // !__WXOSX__
<|endoftext|>


<commit_before>/*
 *  This file is part of Poedit (http://poedit.net)
 *
 *  Copyright (C) 2014 Vaclav Slavik
 *
 *  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 "customcontrols.h"

#include <wx/textwrapper.h>
#include <wx/settings.h>
#include <wx/wupdlock.h>

namespace
{

class LabelWrapper : public wxTextWrapper
{
public:
    void WrapAndSetLabel(wxWindow *window, const wxString& text, int widthMax)
    {
        m_text.clear();
        Wrap(window, text, widthMax);
        window->SetLabel(m_text);
    }

    void OnOutputLine(const wxString& line) override
        { m_text += line; }
    void OnNewLine() override
        { m_text += '\n'; }

    wxString m_text;
};

} // anonymous namespace

AutoWrappingText::AutoWrappingText(wxWindow *parent, const wxString& label)
    : wxStaticText(parent, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxST_NO_AUTORESIZE),
      m_text(label),
      m_wrapWidth(-1)
{
    m_text.Replace("\n", " ");
    SetLabel(m_text);

    SetInitialSize(wxSize(10,10));
    Bind(wxEVT_SIZE, &ExplanationLabel::OnSize, this);
}

void AutoWrappingText::SetAlignment(int align)
{
    if (GetWindowStyleFlag() & align)
        return;
    SetWindowStyleFlag(wxST_NO_AUTORESIZE | align);
}

void AutoWrappingText::SetAndWrapLabel(const wxString& label)
{
    wxWindowUpdateLocker lock(this);
    m_text = label;
    m_wrapWidth = GetSize().x;
    LabelWrapper().WrapAndSetLabel(this, label, m_wrapWidth);

    InvalidateBestSize();
    SetMinSize(wxDefaultSize);
    SetMinSize(GetBestSize());
}

void AutoWrappingText::OnSize(wxSizeEvent& e)
{
    e.Skip();
    int w = e.GetSize().x - 4;
    if (w == m_wrapWidth)
        return;

    wxWindowUpdateLocker lock(this);

    m_wrapWidth = w;
    LabelWrapper().WrapAndSetLabel(this, m_text, w);

    InvalidateBestSize();
    SetMinSize(wxDefaultSize);
    SetMinSize(GetBestSize());
}

ExplanationLabel::ExplanationLabel(wxWindow *parent, const wxString& label)
    : AutoWrappingText(parent, label)
{
#if defined(__WXOSX__) || defined(__WXGTK__)
    SetWindowVariant(wxWINDOW_VARIANT_SMALL);
#endif
    SetForegroundColour(GetTextColor());
}

wxColour ExplanationLabel::GetTextColor()
{
#if defined(__WXOSX__)
    return wxColour("#777777");
#else
    return wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT);
#endif
}

LearnMoreLink::LearnMoreLink(wxWindow *parent, const wxString& url, wxString label)
{
    if (label.empty())
    {
#ifdef __WXMSW__
        label = _("Learn more");
#else
        label = _("Learn More");
#endif
    }

    wxHyperlinkCtrl::Create(parent, wxID_ANY, label, url);
    SetNormalColour("#2F79BE");
    SetVisitedColour("#2F79BE");
    SetHoverColour("#3D8DD5");

#ifdef __WXOSX__
    SetWindowVariant(wxWINDOW_VARIANT_SMALL);
    SetFont(GetFont().Underlined());
#endif
}

wxObject *LearnMoreLinkXmlHandler::DoCreateResource()
{
    auto w = new LearnMoreLink(m_parentAsWindow, GetText("url"), GetText("label"));
    SetupWindow(w);
    return w;
}

bool LearnMoreLinkXmlHandler::CanHandle(wxXmlNode *node)
{
    return IsOfClass(node, "LearnMoreLink");
}
<commit_msg>Don't use gray for ExplanationLabel under GTK+<commit_after>/*
 *  This file is part of Poedit (http://poedit.net)
 *
 *  Copyright (C) 2014 Vaclav Slavik
 *
 *  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 "customcontrols.h"

#include <wx/textwrapper.h>
#include <wx/settings.h>
#include <wx/wupdlock.h>

namespace
{

class LabelWrapper : public wxTextWrapper
{
public:
    void WrapAndSetLabel(wxWindow *window, const wxString& text, int widthMax)
    {
        m_text.clear();
        Wrap(window, text, widthMax);
        window->SetLabel(m_text);
    }

    void OnOutputLine(const wxString& line) override
        { m_text += line; }
    void OnNewLine() override
        { m_text += '\n'; }

    wxString m_text;
};

} // anonymous namespace

AutoWrappingText::AutoWrappingText(wxWindow *parent, const wxString& label)
    : wxStaticText(parent, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxST_NO_AUTORESIZE),
      m_text(label),
      m_wrapWidth(-1)
{
    m_text.Replace("\n", " ");
    SetLabel(m_text);

    SetInitialSize(wxSize(10,10));
    Bind(wxEVT_SIZE, &ExplanationLabel::OnSize, this);
}

void AutoWrappingText::SetAlignment(int align)
{
    if (GetWindowStyleFlag() & align)
        return;
    SetWindowStyleFlag(wxST_NO_AUTORESIZE | align);
}

void AutoWrappingText::SetAndWrapLabel(const wxString& label)
{
    wxWindowUpdateLocker lock(this);
    m_text = label;
    m_wrapWidth = GetSize().x;
    LabelWrapper().WrapAndSetLabel(this, label, m_wrapWidth);

    InvalidateBestSize();
    SetMinSize(wxDefaultSize);
    SetMinSize(GetBestSize());
}

void AutoWrappingText::OnSize(wxSizeEvent& e)
{
    e.Skip();
    int w = e.GetSize().x - 4;
    if (w == m_wrapWidth)
        return;

    wxWindowUpdateLocker lock(this);

    m_wrapWidth = w;
    LabelWrapper().WrapAndSetLabel(this, m_text, w);

    InvalidateBestSize();
    SetMinSize(wxDefaultSize);
    SetMinSize(GetBestSize());
}

ExplanationLabel::ExplanationLabel(wxWindow *parent, const wxString& label)
    : AutoWrappingText(parent, label)
{
#if defined(__WXOSX__) || defined(__WXGTK__)
    SetWindowVariant(wxWINDOW_VARIANT_SMALL);
#endif

#ifndef __WXGTK__
    SetForegroundColour(GetTextColor());
#endif
}

wxColour ExplanationLabel::GetTextColor()
{
#if defined(__WXOSX__)
    return wxColour("#777777");
#elif defined(__WXGTK__)
    return wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
#else
    return wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT);
#endif
}

LearnMoreLink::LearnMoreLink(wxWindow *parent, const wxString& url, wxString label)
{
    if (label.empty())
    {
#ifdef __WXMSW__
        label = _("Learn more");
#else
        label = _("Learn More");
#endif
    }

    wxHyperlinkCtrl::Create(parent, wxID_ANY, label, url);
    SetNormalColour("#2F79BE");
    SetVisitedColour("#2F79BE");
    SetHoverColour("#3D8DD5");

#ifdef __WXOSX__
    SetWindowVariant(wxWINDOW_VARIANT_SMALL);
    SetFont(GetFont().Underlined());
#endif
}

wxObject *LearnMoreLinkXmlHandler::DoCreateResource()
{
    auto w = new LearnMoreLink(m_parentAsWindow, GetText("url"), GetText("label"));
    SetupWindow(w);
    return w;
}

bool LearnMoreLinkXmlHandler::CanHandle(wxXmlNode *node)
{
    return IsOfClass(node, "LearnMoreLink");
}
<|endoftext|>


<commit_before>/* Copyright (c) 2010 - 2021 Advanced Micro Devices, Inc.

 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. */

#ifndef VERSIONS_HPP_
#define VERSIONS_HPP_

#include "utils/macros.hpp"

#ifndef AMD_PLATFORM_NAME
#define AMD_PLATFORM_NAME "AMD Accelerated Parallel Processing"
#endif  // AMD_PLATFORM_NAME

#ifndef AMD_PLATFORM_BUILD_NUMBER
#define AMD_PLATFORM_BUILD_NUMBER 3494
#endif  // AMD_PLATFORM_BUILD_NUMBER

#ifndef AMD_PLATFORM_REVISION_NUMBER
#define AMD_PLATFORM_REVISION_NUMBER 0
#endif  // AMD_PLATFORM_REVISION_NUMBER

#ifndef AMD_PLATFORM_RELEASE_INFO
#define AMD_PLATFORM_RELEASE_INFO
#endif  // AMD_PLATFORM_RELEASE_INFO

#define AMD_BUILD_STRING                                                                           \
  XSTR(AMD_PLATFORM_BUILD_NUMBER)                                                                  \
  "." XSTR(AMD_PLATFORM_REVISION_NUMBER)

#ifndef AMD_PLATFORM_INFO
#define AMD_PLATFORM_INFO                                                                          \
  "AMD-APP" AMD_PLATFORM_RELEASE_INFO DEBUG_ONLY(                                                  \
      "." IF(IS_OPTIMIZED, "opt", "dbg")) " (" AMD_BUILD_STRING ")"
#endif  // ATI_PLATFORM_INFO

#endif  // VERSIONS_HPP_
<commit_msg>SWDEV-2 - Change OpenCL version number from  3494 to 3495<commit_after>/* Copyright (c) 2010 - 2021 Advanced Micro Devices, Inc.

 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. */

#ifndef VERSIONS_HPP_
#define VERSIONS_HPP_

#include "utils/macros.hpp"

#ifndef AMD_PLATFORM_NAME
#define AMD_PLATFORM_NAME "AMD Accelerated Parallel Processing"
#endif  // AMD_PLATFORM_NAME

#ifndef AMD_PLATFORM_BUILD_NUMBER
#define AMD_PLATFORM_BUILD_NUMBER 3495
#endif  // AMD_PLATFORM_BUILD_NUMBER

#ifndef AMD_PLATFORM_REVISION_NUMBER
#define AMD_PLATFORM_REVISION_NUMBER 0
#endif  // AMD_PLATFORM_REVISION_NUMBER

#ifndef AMD_PLATFORM_RELEASE_INFO
#define AMD_PLATFORM_RELEASE_INFO
#endif  // AMD_PLATFORM_RELEASE_INFO

#define AMD_BUILD_STRING                                                                           \
  XSTR(AMD_PLATFORM_BUILD_NUMBER)                                                                  \
  "." XSTR(AMD_PLATFORM_REVISION_NUMBER)

#ifndef AMD_PLATFORM_INFO
#define AMD_PLATFORM_INFO                                                                          \
  "AMD-APP" AMD_PLATFORM_RELEASE_INFO DEBUG_ONLY(                                                  \
      "." IF(IS_OPTIMIZED, "opt", "dbg")) " (" AMD_BUILD_STRING ")"
#endif  // ATI_PLATFORM_INFO

#endif  // VERSIONS_HPP_
<|endoftext|>


<commit_before>/* Copyright (c) 2010-present Advanced Micro Devices, Inc.

 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. */

#ifndef VERSIONS_HPP_
#define VERSIONS_HPP_

#include "utils/macros.hpp"

#ifndef AMD_PLATFORM_NAME
#define AMD_PLATFORM_NAME "AMD Accelerated Parallel Processing"
#endif  // AMD_PLATFORM_NAME

#ifndef AMD_PLATFORM_BUILD_NUMBER
#define AMD_PLATFORM_BUILD_NUMBER 3127
#endif  // AMD_PLATFORM_BUILD_NUMBER

#ifndef AMD_PLATFORM_REVISION_NUMBER
#define AMD_PLATFORM_REVISION_NUMBER 0
#endif  // AMD_PLATFORM_REVISION_NUMBER

#ifndef AMD_PLATFORM_RELEASE_INFO
#define AMD_PLATFORM_RELEASE_INFO
#endif  // AMD_PLATFORM_RELEASE_INFO

#define AMD_BUILD_STRING                                                                           \
  XSTR(AMD_PLATFORM_BUILD_NUMBER)                                                                  \
  "." XSTR(AMD_PLATFORM_REVISION_NUMBER)

#ifndef AMD_PLATFORM_INFO
#define AMD_PLATFORM_INFO                                                                          \
  "AMD-APP" AMD_PLATFORM_RELEASE_INFO DEBUG_ONLY(                                                  \
      "." IF(IS_OPTIMIZED, "opt", "dbg")) " (" AMD_BUILD_STRING ")"
#endif  // ATI_PLATFORM_INFO

#endif  // VERSIONS_HPP_
<commit_msg>SWDEV-2 - Change OpenCL version number from  3127 to 3128<commit_after>/* Copyright (c) 2010-present Advanced Micro Devices, Inc.

 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. */

#ifndef VERSIONS_HPP_
#define VERSIONS_HPP_

#include "utils/macros.hpp"

#ifndef AMD_PLATFORM_NAME
#define AMD_PLATFORM_NAME "AMD Accelerated Parallel Processing"
#endif  // AMD_PLATFORM_NAME

#ifndef AMD_PLATFORM_BUILD_NUMBER
#define AMD_PLATFORM_BUILD_NUMBER 3128
#endif  // AMD_PLATFORM_BUILD_NUMBER

#ifndef AMD_PLATFORM_REVISION_NUMBER
#define AMD_PLATFORM_REVISION_NUMBER 0
#endif  // AMD_PLATFORM_REVISION_NUMBER

#ifndef AMD_PLATFORM_RELEASE_INFO
#define AMD_PLATFORM_RELEASE_INFO
#endif  // AMD_PLATFORM_RELEASE_INFO

#define AMD_BUILD_STRING                                                                           \
  XSTR(AMD_PLATFORM_BUILD_NUMBER)                                                                  \
  "." XSTR(AMD_PLATFORM_REVISION_NUMBER)

#ifndef AMD_PLATFORM_INFO
#define AMD_PLATFORM_INFO                                                                          \
  "AMD-APP" AMD_PLATFORM_RELEASE_INFO DEBUG_ONLY(                                                  \
      "." IF(IS_OPTIMIZED, "opt", "dbg")) " (" AMD_BUILD_STRING ")"
#endif  // ATI_PLATFORM_INFO

#endif  // VERSIONS_HPP_
<|endoftext|>


<commit_before>/* Copyright (c) 2010-present Advanced Micro Devices, Inc.

 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. */

#ifndef VERSIONS_HPP_
#define VERSIONS_HPP_

#include "utils/macros.hpp"

#ifndef AMD_PLATFORM_NAME
#define AMD_PLATFORM_NAME "AMD Accelerated Parallel Processing"
#endif  // AMD_PLATFORM_NAME

#ifndef AMD_PLATFORM_BUILD_NUMBER
#define AMD_PLATFORM_BUILD_NUMBER 3197
#endif  // AMD_PLATFORM_BUILD_NUMBER

#ifndef AMD_PLATFORM_REVISION_NUMBER
#define AMD_PLATFORM_REVISION_NUMBER 0
#endif  // AMD_PLATFORM_REVISION_NUMBER

#ifndef AMD_PLATFORM_RELEASE_INFO
#define AMD_PLATFORM_RELEASE_INFO
#endif  // AMD_PLATFORM_RELEASE_INFO

#define AMD_BUILD_STRING                                                                           \
  XSTR(AMD_PLATFORM_BUILD_NUMBER)                                                                  \
  "." XSTR(AMD_PLATFORM_REVISION_NUMBER)

#ifndef AMD_PLATFORM_INFO
#define AMD_PLATFORM_INFO                                                                          \
  "AMD-APP" AMD_PLATFORM_RELEASE_INFO DEBUG_ONLY(                                                  \
      "." IF(IS_OPTIMIZED, "opt", "dbg")) " (" AMD_BUILD_STRING ")"
#endif  // ATI_PLATFORM_INFO

#endif  // VERSIONS_HPP_
<commit_msg>SWDEV-2 - Change OpenCL version number from  3197 to 3198<commit_after>/* Copyright (c) 2010-present Advanced Micro Devices, Inc.

 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. */

#ifndef VERSIONS_HPP_
#define VERSIONS_HPP_

#include "utils/macros.hpp"

#ifndef AMD_PLATFORM_NAME
#define AMD_PLATFORM_NAME "AMD Accelerated Parallel Processing"
#endif  // AMD_PLATFORM_NAME

#ifndef AMD_PLATFORM_BUILD_NUMBER
#define AMD_PLATFORM_BUILD_NUMBER 3198
#endif  // AMD_PLATFORM_BUILD_NUMBER

#ifndef AMD_PLATFORM_REVISION_NUMBER
#define AMD_PLATFORM_REVISION_NUMBER 0
#endif  // AMD_PLATFORM_REVISION_NUMBER

#ifndef AMD_PLATFORM_RELEASE_INFO
#define AMD_PLATFORM_RELEASE_INFO
#endif  // AMD_PLATFORM_RELEASE_INFO

#define AMD_BUILD_STRING                                                                           \
  XSTR(AMD_PLATFORM_BUILD_NUMBER)                                                                  \
  "." XSTR(AMD_PLATFORM_REVISION_NUMBER)

#ifndef AMD_PLATFORM_INFO
#define AMD_PLATFORM_INFO                                                                          \
  "AMD-APP" AMD_PLATFORM_RELEASE_INFO DEBUG_ONLY(                                                  \
      "." IF(IS_OPTIMIZED, "opt", "dbg")) " (" AMD_BUILD_STRING ")"
#endif  // ATI_PLATFORM_INFO

#endif  // VERSIONS_HPP_
<|endoftext|>


<commit_before>//===--- GlobalSymbolBuilderMain.cpp -----------------------------*- C++-*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// GlobalSymbolBuilder is a tool to generate YAML-format symbols across the
// whole project. This tools is for **experimental** only. Don't use it in
// production code.
//
//===---------------------------------------------------------------------===//

#include "index/Index.h"
#include "index/SymbolCollector.h"
#include "index/SymbolYAML.h"
#include "clang/Frontend/FrontendActions.h"
#include "clang/Index/IndexDataConsumer.h"
#include "clang/Index/IndexingAction.h"
#include "clang/Tooling/CommonOptionsParser.h"
#include "clang/Tooling/Execution.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/ThreadPool.h"

using namespace llvm;
using namespace clang::tooling;
using clang::clangd::SymbolSlab;

namespace clang {
namespace clangd {

static llvm::cl::opt<std::string> AssumedHeaderDir(
    "assume-header-dir",
    llvm::cl::desc("The index includes header that a symbol is defined in. "
                   "If the absolute path cannot be determined (e.g. an "
                   "in-memory VFS) then the relative path is resolved against "
                   "this directory, which must be absolute. If this flag is "
                   "not given, such headers will have relative paths."));

class SymbolIndexActionFactory : public tooling::FrontendActionFactory {
public:
  SymbolIndexActionFactory(tooling::ExecutionContext *Ctx) : Ctx(Ctx) {}

  clang::FrontendAction *create() override {
    // Wraps the index action and reports collected symbols to the execution
    // context at the end of each translation unit.
    class WrappedIndexAction : public WrapperFrontendAction {
    public:
      WrappedIndexAction(std::shared_ptr<SymbolCollector> C,
                         const index::IndexingOptions &Opts,
                         tooling::ExecutionContext *Ctx)
          : WrapperFrontendAction(
                index::createIndexingAction(C, Opts, nullptr)),
            Ctx(Ctx), Collector(C) {}

      void EndSourceFileAction() override {
        WrapperFrontendAction::EndSourceFileAction();

        auto Symbols = Collector->takeSymbols();
        for (const auto &Sym : Symbols) {
          std::string IDStr;
          llvm::raw_string_ostream OS(IDStr);
          OS << Sym.ID;
          Ctx->reportResult(OS.str(), SymbolToYAML(Sym));
        }
      }

    private:
      tooling::ExecutionContext *Ctx;
      std::shared_ptr<SymbolCollector> Collector;
    };

    index::IndexingOptions IndexOpts;
    IndexOpts.SystemSymbolFilter =
        index::IndexingOptions::SystemSymbolFilterKind::All;
    IndexOpts.IndexFunctionLocals = false;
    auto CollectorOpts = SymbolCollector::Options();
    CollectorOpts.FallbackDir = AssumedHeaderDir;
    return new WrappedIndexAction(
        std::make_shared<SymbolCollector>(std::move(CollectorOpts)), IndexOpts,
        Ctx);
  }

  tooling::ExecutionContext *Ctx;
};

} // namespace clangd
} // namespace clang

int main(int argc, const char **argv) {
  llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);

  const char* Overview =
      "This is an **experimental** tool to generate YAML-format "
      "project-wide symbols for clangd (global code completion). It would be "
      "changed and deprecated eventually. Don't use it in production code!";
  auto Executor = clang::tooling::createExecutorFromCommandLineArgs(
      argc, argv, cl::GeneralCategory, Overview);

  if (!Executor) {
    llvm::errs() << llvm::toString(Executor.takeError()) << "\n";
    return 1;
  }

  if (!clang::clangd::AssumedHeaderDir.empty() &&
      !llvm::sys::path::is_absolute(clang::clangd::AssumedHeaderDir)) {
    llvm::errs() << "--assume-header-dir must be an absolute path.\n";
    return 1;
  }

  auto Err = Executor->get()->execute(
      llvm::make_unique<clang::clangd::SymbolIndexActionFactory>(
          Executor->get()->getExecutionContext()));
  if (Err) {
    llvm::errs() << llvm::toString(std::move(Err)) << "\n";
  }

  // Deduplicate the result by key and keep the longest value.
  // FIXME(ioeric): Merge occurrences, rather than just dropping all but one.
  // Definitions and forward declarations have the same key and may both have
  // information. Usage count will need to be aggregated across occurrences,
  // too.
  llvm::StringMap<llvm::StringRef> UniqueSymbols;
  Executor->get()->getToolResults()->forEachResult(
      [&UniqueSymbols](llvm::StringRef Key, llvm::StringRef Value) {
        auto Ret = UniqueSymbols.try_emplace(Key, Value);
        if (!Ret.second) {
          // If key already exists, keep the longest value.
          llvm::StringRef &V = Ret.first->second;
          V = V.size() < Value.size() ? Value : V;
        }
      });
  for (const auto &Sym : UniqueSymbols)
    llvm::outs() << Sym.second;
  return 0;
}
<commit_msg>[clangd] Expclictly set the init value of -assume-header-dir option, NFC.<commit_after>//===--- GlobalSymbolBuilderMain.cpp -----------------------------*- C++-*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// GlobalSymbolBuilder is a tool to generate YAML-format symbols across the
// whole project. This tools is for **experimental** only. Don't use it in
// production code.
//
//===---------------------------------------------------------------------===//

#include "index/Index.h"
#include "index/SymbolCollector.h"
#include "index/SymbolYAML.h"
#include "clang/Frontend/FrontendActions.h"
#include "clang/Index/IndexDataConsumer.h"
#include "clang/Index/IndexingAction.h"
#include "clang/Tooling/CommonOptionsParser.h"
#include "clang/Tooling/Execution.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/ThreadPool.h"

using namespace llvm;
using namespace clang::tooling;
using clang::clangd::SymbolSlab;

namespace clang {
namespace clangd {

static llvm::cl::opt<std::string> AssumedHeaderDir(
    "assume-header-dir",
    llvm::cl::desc("The index includes header that a symbol is defined in. "
                   "If the absolute path cannot be determined (e.g. an "
                   "in-memory VFS) then the relative path is resolved against "
                   "this directory, which must be absolute. If this flag is "
                   "not given, such headers will have relative paths."),
    llvm::cl::init(""));

class SymbolIndexActionFactory : public tooling::FrontendActionFactory {
public:
  SymbolIndexActionFactory(tooling::ExecutionContext *Ctx) : Ctx(Ctx) {}

  clang::FrontendAction *create() override {
    // Wraps the index action and reports collected symbols to the execution
    // context at the end of each translation unit.
    class WrappedIndexAction : public WrapperFrontendAction {
    public:
      WrappedIndexAction(std::shared_ptr<SymbolCollector> C,
                         const index::IndexingOptions &Opts,
                         tooling::ExecutionContext *Ctx)
          : WrapperFrontendAction(
                index::createIndexingAction(C, Opts, nullptr)),
            Ctx(Ctx), Collector(C) {}

      void EndSourceFileAction() override {
        WrapperFrontendAction::EndSourceFileAction();

        auto Symbols = Collector->takeSymbols();
        for (const auto &Sym : Symbols) {
          std::string IDStr;
          llvm::raw_string_ostream OS(IDStr);
          OS << Sym.ID;
          Ctx->reportResult(OS.str(), SymbolToYAML(Sym));
        }
      }

    private:
      tooling::ExecutionContext *Ctx;
      std::shared_ptr<SymbolCollector> Collector;
    };

    index::IndexingOptions IndexOpts;
    IndexOpts.SystemSymbolFilter =
        index::IndexingOptions::SystemSymbolFilterKind::All;
    IndexOpts.IndexFunctionLocals = false;
    auto CollectorOpts = SymbolCollector::Options();
    CollectorOpts.FallbackDir = AssumedHeaderDir;
    return new WrappedIndexAction(
        std::make_shared<SymbolCollector>(std::move(CollectorOpts)), IndexOpts,
        Ctx);
  }

  tooling::ExecutionContext *Ctx;
};

} // namespace clangd
} // namespace clang

int main(int argc, const char **argv) {
  llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);

  const char* Overview =
      "This is an **experimental** tool to generate YAML-format "
      "project-wide symbols for clangd (global code completion). It would be "
      "changed and deprecated eventually. Don't use it in production code!";
  auto Executor = clang::tooling::createExecutorFromCommandLineArgs(
      argc, argv, cl::GeneralCategory, Overview);

  if (!Executor) {
    llvm::errs() << llvm::toString(Executor.takeError()) << "\n";
    return 1;
  }

  if (!clang::clangd::AssumedHeaderDir.empty() &&
      !llvm::sys::path::is_absolute(clang::clangd::AssumedHeaderDir)) {
    llvm::errs() << "--assume-header-dir must be an absolute path.\n";
    return 1;
  }

  auto Err = Executor->get()->execute(
      llvm::make_unique<clang::clangd::SymbolIndexActionFactory>(
          Executor->get()->getExecutionContext()));
  if (Err) {
    llvm::errs() << llvm::toString(std::move(Err)) << "\n";
  }

  // Deduplicate the result by key and keep the longest value.
  // FIXME(ioeric): Merge occurrences, rather than just dropping all but one.
  // Definitions and forward declarations have the same key and may both have
  // information. Usage count will need to be aggregated across occurrences,
  // too.
  llvm::StringMap<llvm::StringRef> UniqueSymbols;
  Executor->get()->getToolResults()->forEachResult(
      [&UniqueSymbols](llvm::StringRef Key, llvm::StringRef Value) {
        auto Ret = UniqueSymbols.try_emplace(Key, Value);
        if (!Ret.second) {
          // If key already exists, keep the longest value.
          llvm::StringRef &V = Ret.first->second;
          V = V.size() < Value.size() ? Value : V;
        }
      });
  for (const auto &Sym : UniqueSymbols)
    llvm::outs() << Sym.second;
  return 0;
}
<|endoftext|>


<commit_before>/*
 * loadervtk.cpp
 *
 *  Created on: Apr 5, 2013
 *      Author: schurade
 */

#include "loadervtk.h"

#include <QDir>
#include <QTextStream>
#include <QDebug>

LoaderVTK::LoaderVTK( QString fn ) :
    m_filename( fn ),
    m_status( "ok" ),
    m_type( 0 ),
    m_primitiveType( 0 ),
    m_numPoints( 0 ),
    m_numPrimitives( 0 ),
    m_primitiveSize( 0 ),
    m_binFileSize( 0 )
{
}

LoaderVTK::~LoaderVTK()
{
}

QString LoaderVTK::getStatus()
{
    return m_status;
}

int LoaderVTK::getPrimitiveType()
{
    return m_primitiveType;
}

QVector<float> LoaderVTK::getPoints()
{
    return m_points;
}

QVector<int> LoaderVTK::getPrimitives()
{
    return m_primitives;
}

QVector<QVector<float> > LoaderVTK::getPointData()
{
    return m_pointData;
}

QVector<QString>LoaderVTK::getDataNames()
{
    return m_dataNames;
}

int LoaderVTK::getNumPoints()
{
    return m_numPoints;
}

int LoaderVTK::getNumPrimitives()
{
    return m_numPrimitives;
}

QString LoaderVTK::readLine( QDataStream& in )
{
    char* s = new char[1];
    QString out;
    do
    {
        in.readRawData( s, 1 );
        out += s[0];
    }
    while( s[0] != '\n' );

    return out.mid( 0, out.size() - 1 );
}

bool LoaderVTK::load()
{
    if ( !exists() )
    {
        return false;
    }
    if ( !open() )
    {
        return false;
    }
    if ( !loadHeader() )
    {
        return false;
    }

    if ( m_type == 1 )
    {
        return loadAscii();
    }
    else if ( m_type == 2 )
    {
        return loadBinary();
    }
    return false;
}

bool LoaderVTK::exists()
{
    QDir dir( m_filename );

    if ( !dir.exists( dir.absolutePath() ) )
    {
        m_status = "*ERROR* file " + m_filename + " doesn't exist";
        return false;
    }
    else
    {
        return true;
    }
}

bool LoaderVTK::open()
{
    m_file = new QFile( m_filename );
    if ( !m_file->open( QIODevice::ReadOnly ) )
    {
        m_status = "*ERROR* couldn't open file " + m_filename;
        return false;
    }
    else
    {
        return true;
    }
}

bool LoaderVTK::loadHeader()
{
    QString line;
    QStringList tokens;

    QTextStream in( m_file );
    if( !in.atEnd() )
    {
        m_header.push_back( in.readLine() );
        m_header.push_back( in.readLine() );
        m_header.push_back( in.readLine() );
        m_header.push_back( in.readLine() );
    }
    if( m_header.size() != 4 )
    {
        m_status = "*ERROR* couldn't load header of file " + m_filename;
        return false;
    }
    if ( m_header[0] != "# vtk DataFile Version 3.0" )
    {
        m_status = "*ERROR* unexpected field at header line 0 (vtk version) in file " + m_filename;
        return false;
    }

    // skip line 1

    if ( m_header[2] == "ASCII" )
    {
        m_type = 1;
    }
    else if ( m_header[2] == "BINARY" )
    {
        m_type = 2;
    }
    else
    {
        m_status = "*ERROR* unexpected field at header line 2 (data representation) in file " + m_filename;
        return false;
    }

    if ( m_header[3] != "DATASET POLYDATA" )
    {
        m_status = "*ERROR* unexpected field at header line 3 (dataset type) in file " + m_filename;
        return false;
    }
    return true;
}

bool LoaderVTK::loadAscii()
{
    if ( !loadPointsAscii() )
    {
        return false;
    }
    if ( !loadPrimitivesAscii() )
    {
        return false;
    }
    loadPointDataAscii();

    return true;
}

bool LoaderVTK::loadPointsAscii()
{
    QString line;
    QStringList tokens;

    m_file->seek( 0 );
    QTextStream in( m_file );

    while( !in.atEnd() )
    {
        line = in.readLine();
        if ( ( line.startsWith( "POINTS") && line.endsWith( "float") ) )
        {
            break;
        }
    }
    if ( ( line.startsWith( "POINTS") && line.endsWith( "float") ) )
    {
        tokens = line.split( " ", QString::SkipEmptyParts );
        m_numPoints = tokens[1].toInt();
    }
    else
    {
        m_status = "*ERROR* while searching for point declaration, unexpected EOF in file " + m_filename;
        return false;
    }

    while( !in.atEnd() )
    {
        line = in.readLine();
        tokens = line.split( " ", QString::SkipEmptyParts );

        for ( int i = 0; i < tokens.size(); ++i )
        {
            m_points.push_back( tokens[i].toFloat() );
        }

        if ( m_points.size() == m_numPoints * 3 )
        {
            break;
        }
    }
    if ( m_points.size() != ( m_numPoints * 3 ) )
    {
        m_status = "*ERROR* unexpected EOF while loading POINTS in file" + m_filename;
        return false;
    }

    return true;
}

bool LoaderVTK::loadPrimitivesAscii()
{
    QString line;
    QStringList tokens;
    m_file->seek( 0 );
    QTextStream in( m_file );

    while( !in.atEnd() )
    {
        line = in.readLine();
        if ( line.startsWith( "POLYGONS") || line.startsWith( "LINES") )
        {
            break;
        }
    }
    if ( line.startsWith( "POLYGONS") )
    {
        m_primitiveType = 1;
    }
    else if ( line.startsWith( "LINES") )
    {
        m_primitiveType = 2;
    }
    else
    {
        m_status = "*ERROR* while searching for primitive declaration, unexpected EOF in file " + m_filename;
        return false;
    }

    tokens = line.split( " ", QString::SkipEmptyParts );
    m_numPrimitives = tokens[1].toInt();
    m_primitiveSize = tokens[2].toInt();

    while( !in.atEnd() )
    {
        line = in.readLine();
        tokens = line.split( " ", QString::SkipEmptyParts );

        for ( int i = 0; i < tokens.size(); ++i )
        {
            m_primitives.push_back( tokens[i].toInt() );
        }

        if ( m_primitives.size() == m_primitiveSize )
        {
            break;
        }
    }
    if ( m_primitives.size() != m_primitiveSize )
    {
        m_status = "*ERROR* unexpected EOF while loading primitives in file" + m_filename;
        return false;
    }

    return true;
}

bool LoaderVTK::loadPointDataAscii()
{
    QString line;
    QStringList tokens;
    m_file->seek( 0 );
    QTextStream in( m_file );

    while( !in.atEnd() )
    {
        line = in.readLine();
        if ( line.startsWith( "POINT_DATA") )
        {
            break;
        }
    }

    while( !in.atEnd() )
    {
        line = in.readLine();
        if ( line.startsWith( "FIELD") )
        {
            break;
        }
    }

    if ( line.startsWith( "FIELD") )
    {
        tokens = line.split( " ", QString::SkipEmptyParts );
        int numFields = tokens.last().toInt();

        for( int curField = 0; curField < numFields; ++curField )
        {
            line = in.readLine();
            tokens = line.split( " ", QString::SkipEmptyParts );

            int entrySize = tokens[1].toInt();
            int numEntries = tokens[2].toInt();

            if ( numEntries == m_numPoints )
            {
                m_dataNames.push_back( tokens[0] );

                QVector<float> data;
                while( !in.atEnd() )
                {
                    line = in.readLine();
                    tokens = line.split( " ", QString::SkipEmptyParts );

                    for ( int i = 0; i < tokens.size(); ++i )
                    {
                        data.push_back( tokens[i].toFloat() );
                    }

                    if ( data.size() ==  ( m_numPoints * entrySize ) )
                    {
                        break;
                    }
                }
                m_pointData.push_back( data );

            }
        }
        return true;
    }
    else
    {
        return false;
    }
    return false;
}

bool LoaderVTK::loadBinary()
{
    QDataStream in( m_file );
    m_binFileSize = m_file->size();
    qDebug() << m_binFileSize;
    m_binaryFile = new char[ m_binFileSize ];
    m_file->seek( 0 );
    int bytesRead = in.readRawData( m_binaryFile, m_binFileSize );

    qDebug() << "read " << bytesRead << " bytes from binary file into buffer";

    if ( !loadPointsBinary() )
    {
        return false;
    }
    if ( !loadPrimitivesBinary() )
    {
        return false;
    }
    return true;
}

bool LoaderVTK::loadPointsBinary()
{
    int pos = searchBinaryFile( "POINTS" );
    QString line;
    QStringList tokens;
    if ( pos != -1 )
    {
        line = getStringFromBinaryFile( pos );
        qDebug() << line;
        tokens = line.split( " ", QString::SkipEmptyParts );
        if ( ( tokens.size() == 3 ) && line.startsWith( "POINTS") && line.endsWith( "float") )
        {
            m_numPoints = tokens[1].toInt();
        }
        else
        {
            m_status = "*ERROR* unexpected field in points declaration " + m_filename;
            return false;
        }
    }
    else
    {
        m_status = "*ERROR* while searching for point declaration, unexpected EOF in file " + m_filename;
        return false;
    }

    char* pointField = new char[m_numPoints * sizeof( float ) * 3];

    int begin = pos + line.size() + 1;
    int end = begin + m_numPoints * sizeof( float ) * 3;
    int i, j;
    for ( i = begin, j = 0; i < end; ++i, ++j )
    {
        pointField[j] = m_binaryFile[i];
    }

    float* rawPointData = reinterpret_cast<float*>( pointField );
    switchByteOrderOfArray< float >( rawPointData, m_numPoints * 3 );

    m_points.resize( m_numPoints * 3 );
    for ( int k = 0; k < m_points.size(); ++k )
    {
        m_points[k] = rawPointData[k];
    }

    return true;
}

bool LoaderVTK::loadPrimitivesBinary()
{
    QString line;
    QStringList tokens;

    int pos = searchBinaryFile( "POLYGONS" );

    if ( pos != -1 )
    {
        m_primitiveType = 1;
    }

    pos = searchBinaryFile( "LINES" );

    if ( pos != -1 )
    {
        m_primitiveType = 2;
    }

    if ( m_primitiveType == 0 )
    {
        m_status = "*ERROR* while searching for primitive declaration, unexpected EOF in file " + m_filename;
        return false;
    }

    line = getStringFromBinaryFile( pos );
    qDebug() << line;
    tokens = line.split( " ", QString::SkipEmptyParts );

    if ( tokens.size() == 3 )
    {
        m_numPrimitives = tokens[1].toInt();
        m_primitiveSize = tokens[2].toInt();
    }
    else
    {
        m_status = "*ERROR* unexpected field in primitive declaration " + m_filename;
        return false;
    }

    char* primitiveField = new char[ m_primitiveSize * sizeof( int ) ];

    int begin = pos + line.size() + 1;
    int end = begin + m_primitiveSize * sizeof( int );

    int i, j;
    for ( i = begin, j = 0; i < end; ++i, ++j )
    {
        primitiveField[j] = m_binaryFile[i];
    }

    int* rawPrimitiveData = reinterpret_cast<int*>( primitiveField );
    switchByteOrderOfArray< int >( rawPrimitiveData, m_primitiveSize );

    m_primitives.resize( m_primitiveSize );

    for ( int k = 0; k < m_primitiveSize; ++k )
    {
        m_primitives[k] = rawPrimitiveData[k];
    }
    qDebug() << "done loading primitive field";
    return true;
}

int LoaderVTK::searchBinaryFile( QString string )
{
    char first = string.at( 0 ).toLatin1();
    int found = -1;
    for ( int i = 0; i < m_binFileSize; ++i )
    {
        if ( m_binaryFile[i] == first )
        {
            if ( testPos( i, string ) )
            {
                found = i;
                break;
            }
        }
    }
    return found;
}

bool LoaderVTK::testPos( int pos, QString string )
{
    bool match = true;
    for ( int i = 0; i < string.size(); ++i )
    {
        if ( m_binaryFile[pos + i] != string.at( i ).toLatin1() )
        {
            match = false;
            break;
        }
    }
    return match;
}

QString LoaderVTK::getStringFromBinaryFile( int pos )
{
    QString out;
    int i = 0;
    while( m_binaryFile[ pos + i ] != '\n' )
    {
        out += m_binaryFile[ pos + i++ ];
    }
    return out;
}
<commit_msg>fix vtk loader for binary polygon data<commit_after>/*
 * loadervtk.cpp
 *
 *  Created on: Apr 5, 2013
 *      Author: schurade
 */

#include "loadervtk.h"

#include <QDir>
#include <QTextStream>
#include <QDebug>

LoaderVTK::LoaderVTK( QString fn ) :
    m_filename( fn ),
    m_status( "ok" ),
    m_type( 0 ),
    m_primitiveType( 0 ),
    m_numPoints( 0 ),
    m_numPrimitives( 0 ),
    m_primitiveSize( 0 ),
    m_binFileSize( 0 )
{
}

LoaderVTK::~LoaderVTK()
{
}

QString LoaderVTK::getStatus()
{
    return m_status;
}

int LoaderVTK::getPrimitiveType()
{
    return m_primitiveType;
}

QVector<float> LoaderVTK::getPoints()
{
    return m_points;
}

QVector<int> LoaderVTK::getPrimitives()
{
    return m_primitives;
}

QVector<QVector<float> > LoaderVTK::getPointData()
{
    return m_pointData;
}

QVector<QString>LoaderVTK::getDataNames()
{
    return m_dataNames;
}

int LoaderVTK::getNumPoints()
{
    return m_numPoints;
}

int LoaderVTK::getNumPrimitives()
{
    return m_numPrimitives;
}

QString LoaderVTK::readLine( QDataStream& in )
{
    char* s = new char[1];
    QString out;
    do
    {
        in.readRawData( s, 1 );
        out += s[0];
    }
    while( s[0] != '\n' );

    return out.mid( 0, out.size() - 1 );
}

bool LoaderVTK::load()
{
    if ( !exists() )
    {
        return false;
    }
    if ( !open() )
    {
        return false;
    }
    if ( !loadHeader() )
    {
        return false;
    }

    if ( m_type == 1 )
    {
        return loadAscii();
    }
    else if ( m_type == 2 )
    {
        return loadBinary();
    }
    return false;
}

bool LoaderVTK::exists()
{
    QDir dir( m_filename );

    if ( !dir.exists( dir.absolutePath() ) )
    {
        m_status = "*ERROR* file " + m_filename + " doesn't exist";
        return false;
    }
    else
    {
        return true;
    }
}

bool LoaderVTK::open()
{
    m_file = new QFile( m_filename );
    if ( !m_file->open( QIODevice::ReadOnly ) )
    {
        m_status = "*ERROR* couldn't open file " + m_filename;
        return false;
    }
    else
    {
        return true;
    }
}

bool LoaderVTK::loadHeader()
{
    QString line;
    QStringList tokens;

    QTextStream in( m_file );
    if( !in.atEnd() )
    {
        m_header.push_back( in.readLine() );
        m_header.push_back( in.readLine() );
        m_header.push_back( in.readLine() );
        m_header.push_back( in.readLine() );
    }
    if( m_header.size() != 4 )
    {
        m_status = "*ERROR* couldn't load header of file " + m_filename;
        return false;
    }
    if ( m_header[0] != "# vtk DataFile Version 3.0" )
    {
        m_status = "*ERROR* unexpected field at header line 0 (vtk version) in file " + m_filename;
        return false;
    }

    // skip line 1

    if ( m_header[2] == "ASCII" )
    {
        m_type = 1;
    }
    else if ( m_header[2] == "BINARY" )
    {
        m_type = 2;
    }
    else
    {
        m_status = "*ERROR* unexpected field at header line 2 (data representation) in file " + m_filename;
        return false;
    }

    if ( m_header[3] != "DATASET POLYDATA" )
    {
        m_status = "*ERROR* unexpected field at header line 3 (dataset type) in file " + m_filename;
        return false;
    }
    return true;
}

bool LoaderVTK::loadAscii()
{
    if ( !loadPointsAscii() )
    {
        return false;
    }
    if ( !loadPrimitivesAscii() )
    {
        return false;
    }
    loadPointDataAscii();

    return true;
}

bool LoaderVTK::loadPointsAscii()
{
    QString line;
    QStringList tokens;

    m_file->seek( 0 );
    QTextStream in( m_file );

    while( !in.atEnd() )
    {
        line = in.readLine();
        if ( ( line.startsWith( "POINTS") && line.endsWith( "float") ) )
        {
            break;
        }
    }
    if ( ( line.startsWith( "POINTS") && line.endsWith( "float") ) )
    {
        tokens = line.split( " ", QString::SkipEmptyParts );
        m_numPoints = tokens[1].toInt();
    }
    else
    {
        m_status = "*ERROR* while searching for point declaration, unexpected EOF in file " + m_filename;
        return false;
    }

    while( !in.atEnd() )
    {
        line = in.readLine();
        tokens = line.split( " ", QString::SkipEmptyParts );

        for ( int i = 0; i < tokens.size(); ++i )
        {
            m_points.push_back( tokens[i].toFloat() );
        }

        if ( m_points.size() == m_numPoints * 3 )
        {
            break;
        }
    }
    if ( m_points.size() != ( m_numPoints * 3 ) )
    {
        m_status = "*ERROR* unexpected EOF while loading POINTS in file" + m_filename;
        return false;
    }

    return true;
}

bool LoaderVTK::loadPrimitivesAscii()
{
    QString line;
    QStringList tokens;
    m_file->seek( 0 );
    QTextStream in( m_file );

    while( !in.atEnd() )
    {
        line = in.readLine();
        if ( line.startsWith( "POLYGONS") || line.startsWith( "LINES") )
        {
            break;
        }
    }
    if ( line.startsWith( "POLYGONS") )
    {
        m_primitiveType = 1;
    }
    else if ( line.startsWith( "LINES") )
    {
        m_primitiveType = 2;
    }
    else
    {
        m_status = "*ERROR* while searching for primitive declaration, unexpected EOF in file " + m_filename;
        return false;
    }

    tokens = line.split( " ", QString::SkipEmptyParts );
    m_numPrimitives = tokens[1].toInt();
    m_primitiveSize = tokens[2].toInt();

    while( !in.atEnd() )
    {
        line = in.readLine();
        tokens = line.split( " ", QString::SkipEmptyParts );

        for ( int i = 0; i < tokens.size(); ++i )
        {
            m_primitives.push_back( tokens[i].toInt() );
        }

        if ( m_primitives.size() == m_primitiveSize )
        {
            break;
        }
    }
    if ( m_primitives.size() != m_primitiveSize )
    {
        m_status = "*ERROR* unexpected EOF while loading primitives in file" + m_filename;
        return false;
    }

    return true;
}

bool LoaderVTK::loadPointDataAscii()
{
    QString line;
    QStringList tokens;
    m_file->seek( 0 );
    QTextStream in( m_file );

    while( !in.atEnd() )
    {
        line = in.readLine();
        if ( line.startsWith( "POINT_DATA") )
        {
            break;
        }
    }

    while( !in.atEnd() )
    {
        line = in.readLine();
        if ( line.startsWith( "FIELD") )
        {
            break;
        }
    }

    if ( line.startsWith( "FIELD") )
    {
        tokens = line.split( " ", QString::SkipEmptyParts );
        int numFields = tokens.last().toInt();

        for( int curField = 0; curField < numFields; ++curField )
        {
            line = in.readLine();
            tokens = line.split( " ", QString::SkipEmptyParts );

            int entrySize = tokens[1].toInt();
            int numEntries = tokens[2].toInt();

            if ( numEntries == m_numPoints )
            {
                m_dataNames.push_back( tokens[0] );

                QVector<float> data;
                while( !in.atEnd() )
                {
                    line = in.readLine();
                    tokens = line.split( " ", QString::SkipEmptyParts );

                    for ( int i = 0; i < tokens.size(); ++i )
                    {
                        data.push_back( tokens[i].toFloat() );
                    }

                    if ( data.size() ==  ( m_numPoints * entrySize ) )
                    {
                        break;
                    }
                }
                m_pointData.push_back( data );

            }
        }
        return true;
    }
    else
    {
        return false;
    }
    return false;
}

bool LoaderVTK::loadBinary()
{
    QDataStream in( m_file );
    m_binFileSize = m_file->size();
    qDebug() << m_binFileSize;
    m_binaryFile = new char[ m_binFileSize ];
    m_file->seek( 0 );
    int bytesRead = in.readRawData( m_binaryFile, m_binFileSize );

    qDebug() << "read " << bytesRead << " bytes from binary file into buffer";

    if ( !loadPointsBinary() )
    {
        return false;
    }
    if ( !loadPrimitivesBinary() )
    {
        return false;
    }
    return true;
}

bool LoaderVTK::loadPointsBinary()
{
    int pos = searchBinaryFile( "POINTS" );
    QString line;
    QStringList tokens;
    if ( pos != -1 )
    {
        line = getStringFromBinaryFile( pos );
        qDebug() << line;
        tokens = line.split( " ", QString::SkipEmptyParts );
        if ( ( tokens.size() == 3 ) && line.startsWith( "POINTS") && line.endsWith( "float") )
        {
            m_numPoints = tokens[1].toInt();
        }
        else
        {
            m_status = "*ERROR* unexpected field in points declaration " + m_filename;
            return false;
        }
    }
    else
    {
        m_status = "*ERROR* while searching for point declaration, unexpected EOF in file " + m_filename;
        return false;
    }

    char* pointField = new char[m_numPoints * sizeof( float ) * 3];

    int begin = pos + line.size() + 1;
    int end = begin + m_numPoints * sizeof( float ) * 3;
    int i, j;
    for ( i = begin, j = 0; i < end; ++i, ++j )
    {
        pointField[j] = m_binaryFile[i];
    }

    float* rawPointData = reinterpret_cast<float*>( pointField );
    switchByteOrderOfArray< float >( rawPointData, m_numPoints * 3 );

    m_points.resize( m_numPoints * 3 );
    for ( int k = 0; k < m_points.size(); ++k )
    {
        m_points[k] = rawPointData[k];
    }

    delete rawPointData;

    return true;
}

bool LoaderVTK::loadPrimitivesBinary()
{
    QString line;
    QStringList tokens;

    int pos = searchBinaryFile( "POLYGONS" );

    if ( pos != -1 )
    {
        m_primitiveType = 1;
    }
    else
    {
        pos = searchBinaryFile( "LINES" );

        if ( pos != -1 )
        {
            m_primitiveType = 2;
        }
    }

    if ( m_primitiveType == 0 )
    {
        m_status = "*ERROR* while searching for primitive declaration, unexpected EOF in file " + m_filename;
        return false;
    }

    line = getStringFromBinaryFile( pos );
    qDebug() << line;
    tokens = line.split( " ", QString::SkipEmptyParts );

    if ( tokens.size() == 3 )
    {
        m_numPrimitives = tokens[1].toInt();
        m_primitiveSize = tokens[2].toInt();
    }
    else
    {
        m_status = "*ERROR* unexpected field in primitive declaration " + m_filename;
        return false;
    }

    char* primitiveField = new char[ m_primitiveSize * sizeof( int ) ];

    int begin = pos + line.size() + 1;
    int end = begin + m_primitiveSize * sizeof( int );

    int i, j;
    for ( i = begin, j = 0; i < end; ++i, ++j )
    {
        primitiveField[j] = m_binaryFile[i];
    }

    int* rawPrimitiveData = reinterpret_cast<int*>( primitiveField );
    switchByteOrderOfArray< int >( rawPrimitiveData, m_primitiveSize );

    m_primitives.resize( m_primitiveSize );

    for ( int k = 0; k < m_primitiveSize; ++k )
    {
        m_primitives[k] = rawPrimitiveData[k];
    }

    delete rawPrimitiveData;

    return true;
}

int LoaderVTK::searchBinaryFile( QString string )
{
    char first = string.at( 0 ).toLatin1();
    int found = -1;
    for ( int i = 0; i < m_binFileSize; ++i )
    {
        if ( m_binaryFile[i] == first )
        {
            if ( testPos( i, string ) )
            {
                found = i;
                break;
            }
        }
    }
    return found;
}

bool LoaderVTK::testPos( int pos, QString string )
{
    bool match = true;
    for ( int i = 0; i < string.size(); ++i )
    {
        if ( m_binaryFile[pos + i] != string.at( i ).toLatin1() )
        {
            match = false;
            break;
        }
    }
    return match;
}

QString LoaderVTK::getStringFromBinaryFile( int pos )
{
    QString out;
    int i = 0;
    while( m_binaryFile[ pos + i ] != '\n' )
    {
        out += m_binaryFile[ pos + i++ ];
    }
    return out;
}
<|endoftext|>


<commit_before>/*
 * Copyright 2014 CyberVision, Inc.
 *
 * Licensed 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.
 */

#include "kaa/log/LogCollector.hpp"

#include "kaa/gen/EndpointGen.hpp"
#include "kaa/common/UuidGenerator.hpp"
#include "kaa/logging/Log.hpp"
#include "kaa/log/LoggingTransport.hpp"
#include "kaa/log/MemoryLogStorage.hpp"
#include "kaa/log/DefaultLogUploadStrategy.hpp"
#include "kaa/common/exception/TransportNotFoundException.hpp"
#include "kaa/log/LogRecord.hpp"

#ifdef KAA_USE_SQLITE_LOG_STORAGE
#include "kaa/log/SQLiteDBLogStorage.hpp"
#else
#include "kaa/log/MemoryLogStorage.hpp"
#endif

namespace kaa {

LogCollector::LogCollector(IKaaChannelManagerPtr manager)
    : transport_(nullptr), channelManager_(manager), timeoutAccessPointId_(0),
      logUploadCheckTimer_("LogCollector logUploadCheckTimer"), uploadTimer_("LogCollector uploadTimer"),
      timeoutTimer_("LogCollector timeoutTimer")
{
#ifdef KAA_USE_SQLITE_LOG_STORAGE
    storage_.reset(new SQLiteDBLogStorage());
#else
    storage_.reset(new MemoryLogStorage());
#endif
    uploadStrategy_.reset(new DefaultLogUploadStrategy());

    startTimeoutCheckTimer();
}

void LogCollector::startTimeoutCheckTimer() {
    timeoutTimer_.start(uploadStrategy_->getTimeoutCheckPeriod(), [&]
                    {
                            if (isDeliveryTimeout()) {
                                processTimeout();
                            }
                            startTimeoutCheckTimer();
                    });
}

void LogCollector::processTimeout()
{
    uploadStrategy_->onTimeout(*this);

    KAA_LOG_WARN(boost::format("Going to notify log storage of logs delivery timeout..."));

    KAA_MUTEX_LOCKING("timeoutsGuard_");
    KAA_MUTEX_UNIQUE_DECLARE(timeoutsLock, timeoutsGuard_);
    KAA_MUTEX_LOCKED("timeoutsGuard_");

    for (const auto& request : timeouts_) {
        storage_->notifyUploadFailed(request.first);
    }

    timeouts_.clear();

    KAA_MUTEX_UNLOCKING("timeoutsGuard_");
    KAA_UNLOCK(timeoutsLock);
    KAA_MUTEX_UNLOCKED("timeoutsGuard_");

    processLogUploadDecision(uploadStrategy_->isUploadNeeded(storage_->getStatus()));
}

void LogCollector::addLogRecord(const KaaUserLogRecord& record)
{
    LogRecordPtr serializedRecord(new LogRecord(record));

    KAA_MUTEX_LOCKING("storageGuard_");
    KAA_MUTEX_UNIQUE_DECLARE(lock, storageGuard_);
    KAA_MUTEX_LOCKED("storageGuard_");
    storage_->addLogRecord(serializedRecord);

    KAA_MUTEX_UNLOCKING("storageGuard_");
    KAA_UNLOCK(lock);
    KAA_MUTEX_UNLOCKED("storageGuard_");

    processLogUploadDecision(uploadStrategy_->isUploadNeeded(storage_->getStatus()));

}

void LogCollector::processLogUploadDecision(LogUploadStrategyDecision decision)
{
    switch (decision) {
    case LogUploadStrategyDecision::UPLOAD: {
        KAA_LOG_DEBUG("Going to upload logs");
        doSync();
        break;
    }
    case LogUploadStrategyDecision::NOOP:
        KAA_LOG_TRACE("Nothing to do now");
        if (storage_->getStatus().getRecordsCount() > 0) {
            logUploadCheckTimer_.stop();
            logUploadCheckTimer_.start(uploadStrategy_->getLogUploadCheckPeriod(),[&]
            {
                processLogUploadDecision(uploadStrategy_->isUploadNeeded(storage_->getStatus()));
            });
        }
        break;
    default:
        KAA_LOG_WARN("Unknown log upload decision");
        break;
    }
}

void LogCollector::setStorage(ILogStoragePtr storage)
{
    if (!storage) {
        KAA_LOG_ERROR("Failed to set log storage: bad data");
        throw KaaException("Bad log storage");
    }

    KAA_MUTEX_LOCKING("storageGuard_");
    KAA_MUTEX_UNIQUE_DECLARE(lock, storageGuard_);
    KAA_MUTEX_LOCKED("storageGuard_");

    KAA_LOG_INFO("New log storage was set");
    storage_ = storage;
}

void LogCollector::setUploadStrategy(ILogUploadStrategyPtr strategy)
{
    if (!strategy) {
        KAA_LOG_ERROR("Failed to set log upload strategy: bad data");
        throw KaaException("Bad log upload strategy");
    }

    KAA_LOG_INFO("New log upload strategy was set");
    uploadStrategy_ = strategy;
}

void LogCollector::doSync()
{
    KAA_MUTEX_LOCKING("transportGuard_");
    KAA_MUTEX_UNIQUE_DECLARE(transportLock, transportGuard_);
    KAA_MUTEX_LOCKED("transportGuard_");

    if (transport_) {
        transport_->sync();
    } else {
        KAA_LOG_ERROR("Failed to upload logs: log transport isn't initialized");
        throw TransportNotFoundException("Log transport isn't set");
    }
}

bool LogCollector::isDeliveryTimeout()
{

    KAA_MUTEX_LOCKING("timeoutsGuard_");
    KAA_MUTEX_UNIQUE_DECLARE(timeoutsLock, timeoutsGuard_);
    KAA_MUTEX_LOCKED("timeoutsGuard_");

    auto now = clock_t::now();

    IDataChannelPtr logChannel = channelManager_->getChannelByTransportType(TransportType::LOGGING);
    std::int32_t currentAccessPointId  = 0;
    if (logChannel) {
        currentAccessPointId = logChannel->getServer()->getAccessPointId();
    }

    bool isTimeout = false;
    timeoutAccessPointId_ = 0;

    for (const auto& request : timeouts_) {
        if (now >= request.second.getTimeoutTime()) {
            KAA_LOG_WARN(boost::format("Log delivery timeout detected, bucket id %li") % request.first);
            isTimeout = true;
            timeoutAccessPointId_ = request.second.getTransportAccessPointId();
            // Check if current access point already has timeout
            if (timeoutAccessPointId_ == currentAccessPointId) {
                break;
            }
        }
    }

    return isTimeout;
}

void LogCollector::addDeliveryTimeout(std::int32_t requestId)
{
    KAA_MUTEX_LOCKING("timeoutsGuard_");
    KAA_MUTEX_UNIQUE_DECLARE(timeoutsLock, timeoutsGuard_);
    KAA_MUTEX_LOCKED("timeoutsGuard_");

    IDataChannelPtr logChannel = channelManager_->getChannelByTransportType(TransportType::LOGGING);
    std::int32_t currentAccessPointId  = 0;
    if (logChannel) {
        currentAccessPointId = logChannel->getServer()->getAccessPointId();
    }
    TimeoutInfo timeoutInfo(currentAccessPointId,
            clock_t::now() + std::chrono::seconds(uploadStrategy_->getTimeout()));

    timeouts_.insert(std::make_pair(requestId, timeoutInfo));
}

bool LogCollector::removeDeliveryTimeout(std::int32_t requestId)
{
    KAA_MUTEX_LOCKING("timeoutsGuard_");
    KAA_MUTEX_UNIQUE_DECLARE(timeoutsLock, timeoutsGuard_);
    KAA_MUTEX_LOCKED("timeoutsGuard_");

    return timeouts_.erase(requestId);
}

std::shared_ptr<LogSyncRequest> LogCollector::getLogUploadRequest()
{
    ILogStorage::RecordPack recordPack;
    std::shared_ptr<LogSyncRequest> request;

    {
        KAA_MUTEX_LOCKING("storageGuard_");
        KAA_MUTEX_UNIQUE_DECLARE(lock, storageGuard_);
        KAA_MUTEX_LOCKED("storageGuard_");

        recordPack = storage_->getRecordBlock(uploadStrategy_->getBatchSize(), uploadStrategy_->getRecordsBatchCount());
    }

    if (!recordPack.second.empty()) {
        request.reset(new LogSyncRequest);
        request->requestId = recordPack.first;

        std::vector<LogEntry> logs;
        logs.reserve(recordPack.second.size());
        for (const auto& log : recordPack.second) {
            logs.push_back(log->getLogEntry());
        }

        request->logEntries.set_array(std::move(logs));
        addDeliveryTimeout(request->requestId);
    }

    return request;
}

void LogCollector::onLogUploadResponse(const LogSyncResponse& response)
{
    KAA_MUTEX_LOCKING("storageGuard_");
    KAA_MUTEX_UNIQUE_DECLARE(storageLock, storageGuard_);
    KAA_MUTEX_LOCKED("storageGuard_");

    if (!response.deliveryStatuses.is_null()) {
        const auto& deliveryStatuses = response.deliveryStatuses.get_array();
        for (const auto& status : deliveryStatuses) {
            if (!removeDeliveryTimeout(status.requestId)) {
                continue;
            }

            if (status.result == SyncResponseResultType::SUCCESS) {
                KAA_LOG_INFO(boost::format("Logs (requestId %ld) successfully delivered") % status.requestId);
                storage_->removeRecordBlock(status.requestId);
            } else {
                storage_->notifyUploadFailed(status.requestId);

                KAA_MUTEX_UNLOCKING("storageGuard_");
                KAA_UNLOCK(storageLock);
                KAA_MUTEX_UNLOCKED("storageGuard_");

                if (!status.errorCode.is_null()) {
                    KAA_LOG_WARN(boost::format("Logs (requestId %ld) failed to deliver (error %d)")
                                            % status.requestId % (int)status.errorCode.get_LogDeliveryErrorCode());
                    uploadStrategy_->onFailure(*this, status.errorCode.get_LogDeliveryErrorCode());
                } else {
                    KAA_LOG_WARN("Log delivery failed, but no error code received");
                }

                KAA_MUTEX_LOCKING("storageGuard_");
                KAA_LOCK(storageLock);
                KAA_MUTEX_LOCKED("storageGuard_");
            }
        }
    }

    KAA_MUTEX_UNLOCKING("storageGuard_");
    KAA_UNLOCK(storageLock);
    KAA_MUTEX_UNLOCKED("storageGuard_");

    processLogUploadDecision(uploadStrategy_->isUploadNeeded(storage_->getStatus()));
}

void LogCollector::setTransport(LoggingTransport* transport)
{
    KAA_MUTEX_LOCKING("transportGuard_");
    KAA_MUTEX_UNIQUE_DECLARE(transportLock, transportGuard_);
    KAA_MUTEX_LOCKED("transportGuard_");

    transport_ = transport;
}

void LogCollector::retryLogUpload()
{
    KAA_LOG_INFO("Going to retry log upload...");
    doSync();
}

void LogCollector::retryLogUpload(std::size_t delay)
{
    KAA_LOG_INFO(boost::format("Schedule log upload with %u second(s) delay ...") % delay);
    uploadTimer_.stop();
    uploadTimer_.start(delay, [&] { doSync(); });
}

void LogCollector::switchAccessPoint()
{
    IDataChannelPtr logChannel = channelManager_->getChannelByTransportType(TransportType::LOGGING);
    if (logChannel) {
        if (timeoutAccessPointId_ == logChannel->getServer()->getAccessPointId()) {
            KAA_LOG_WARN("Try to switch to another Operations server...");
            channelManager_->onServerFailed(logChannel->getServer());
        }
    } else {
        KAA_LOG_ERROR("Can't find LOGGING data channel");
    }
}

}  // namespace kaa

<commit_msg>KAA-535: Add additional pointer checks in isDeliveryTimeout function.<commit_after>/*
 * Copyright 2014 CyberVision, Inc.
 *
 * Licensed 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.
 */

#include "kaa/log/LogCollector.hpp"

#include "kaa/gen/EndpointGen.hpp"
#include "kaa/common/UuidGenerator.hpp"
#include "kaa/logging/Log.hpp"
#include "kaa/log/LoggingTransport.hpp"
#include "kaa/log/MemoryLogStorage.hpp"
#include "kaa/log/DefaultLogUploadStrategy.hpp"
#include "kaa/common/exception/TransportNotFoundException.hpp"
#include "kaa/log/LogRecord.hpp"

#ifdef KAA_USE_SQLITE_LOG_STORAGE
#include "kaa/log/SQLiteDBLogStorage.hpp"
#else
#include "kaa/log/MemoryLogStorage.hpp"
#endif

namespace kaa {

LogCollector::LogCollector(IKaaChannelManagerPtr manager)
    : transport_(nullptr), channelManager_(manager), timeoutAccessPointId_(0),
      logUploadCheckTimer_("LogCollector logUploadCheckTimer"), uploadTimer_("LogCollector uploadTimer"),
      timeoutTimer_("LogCollector timeoutTimer")
{
#ifdef KAA_USE_SQLITE_LOG_STORAGE
    storage_.reset(new SQLiteDBLogStorage());
#else
    storage_.reset(new MemoryLogStorage());
#endif
    uploadStrategy_.reset(new DefaultLogUploadStrategy());

    startTimeoutCheckTimer();
}

void LogCollector::startTimeoutCheckTimer() {
    timeoutTimer_.start(uploadStrategy_->getTimeoutCheckPeriod(), [&]
                    {
                            if (isDeliveryTimeout()) {
                                processTimeout();
                            }
                            startTimeoutCheckTimer();
                    });
}

void LogCollector::processTimeout()
{
    uploadStrategy_->onTimeout(*this);

    KAA_LOG_WARN(boost::format("Going to notify log storage of logs delivery timeout..."));

    KAA_MUTEX_LOCKING("timeoutsGuard_");
    KAA_MUTEX_UNIQUE_DECLARE(timeoutsLock, timeoutsGuard_);
    KAA_MUTEX_LOCKED("timeoutsGuard_");

    for (const auto& request : timeouts_) {
        storage_->notifyUploadFailed(request.first);
    }

    timeouts_.clear();

    KAA_MUTEX_UNLOCKING("timeoutsGuard_");
    KAA_UNLOCK(timeoutsLock);
    KAA_MUTEX_UNLOCKED("timeoutsGuard_");

    processLogUploadDecision(uploadStrategy_->isUploadNeeded(storage_->getStatus()));
}

void LogCollector::addLogRecord(const KaaUserLogRecord& record)
{
    LogRecordPtr serializedRecord(new LogRecord(record));

    KAA_MUTEX_LOCKING("storageGuard_");
    KAA_MUTEX_UNIQUE_DECLARE(lock, storageGuard_);
    KAA_MUTEX_LOCKED("storageGuard_");
    storage_->addLogRecord(serializedRecord);

    KAA_MUTEX_UNLOCKING("storageGuard_");
    KAA_UNLOCK(lock);
    KAA_MUTEX_UNLOCKED("storageGuard_");

    processLogUploadDecision(uploadStrategy_->isUploadNeeded(storage_->getStatus()));

}

void LogCollector::processLogUploadDecision(LogUploadStrategyDecision decision)
{
    switch (decision) {
    case LogUploadStrategyDecision::UPLOAD: {
        KAA_LOG_DEBUG("Going to upload logs");
        doSync();
        break;
    }
    case LogUploadStrategyDecision::NOOP:
        KAA_LOG_TRACE("Nothing to do now");
        if (storage_->getStatus().getRecordsCount() > 0) {
            logUploadCheckTimer_.stop();
            logUploadCheckTimer_.start(uploadStrategy_->getLogUploadCheckPeriod(),[&]
            {
                processLogUploadDecision(uploadStrategy_->isUploadNeeded(storage_->getStatus()));
            });
        }
        break;
    default:
        KAA_LOG_WARN("Unknown log upload decision");
        break;
    }
}

void LogCollector::setStorage(ILogStoragePtr storage)
{
    if (!storage) {
        KAA_LOG_ERROR("Failed to set log storage: bad data");
        throw KaaException("Bad log storage");
    }

    KAA_MUTEX_LOCKING("storageGuard_");
    KAA_MUTEX_UNIQUE_DECLARE(lock, storageGuard_);
    KAA_MUTEX_LOCKED("storageGuard_");

    KAA_LOG_INFO("New log storage was set");
    storage_ = storage;
}

void LogCollector::setUploadStrategy(ILogUploadStrategyPtr strategy)
{
    if (!strategy) {
        KAA_LOG_ERROR("Failed to set log upload strategy: bad data");
        throw KaaException("Bad log upload strategy");
    }

    KAA_LOG_INFO("New log upload strategy was set");
    uploadStrategy_ = strategy;
}

void LogCollector::doSync()
{
    KAA_MUTEX_LOCKING("transportGuard_");
    KAA_MUTEX_UNIQUE_DECLARE(transportLock, transportGuard_);
    KAA_MUTEX_LOCKED("transportGuard_");

    if (transport_) {
        transport_->sync();
    } else {
        KAA_LOG_ERROR("Failed to upload logs: log transport isn't initialized");
        throw TransportNotFoundException("Log transport isn't set");
    }
}

bool LogCollector::isDeliveryTimeout()
{

    KAA_MUTEX_LOCKING("timeoutsGuard_");
    KAA_MUTEX_UNIQUE_DECLARE(timeoutsLock, timeoutsGuard_);
    KAA_MUTEX_LOCKED("timeoutsGuard_");

    auto now = clock_t::now();

    IDataChannelPtr logChannel = channelManager_->getChannelByTransportType(TransportType::LOGGING);
    std::int32_t currentAccessPointId  = 0;
    if (logChannel && logChannel->getServer()) {
        currentAccessPointId = logChannel->getServer()->getAccessPointId();
    }

    bool isTimeout = false;
    timeoutAccessPointId_ = 0;

    for (const auto& request : timeouts_) {
        if (now >= request.second.getTimeoutTime()) {
            KAA_LOG_WARN(boost::format("Log delivery timeout detected, bucket id %li") % request.first);
            isTimeout = true;
            timeoutAccessPointId_ = request.second.getTransportAccessPointId();
            // Check if current access point already has timeout
            if (timeoutAccessPointId_ == currentAccessPointId) {
                break;
            }
        }
    }

    return isTimeout;
}

void LogCollector::addDeliveryTimeout(std::int32_t requestId)
{
    KAA_MUTEX_LOCKING("timeoutsGuard_");
    KAA_MUTEX_UNIQUE_DECLARE(timeoutsLock, timeoutsGuard_);
    KAA_MUTEX_LOCKED("timeoutsGuard_");

    IDataChannelPtr logChannel = channelManager_->getChannelByTransportType(TransportType::LOGGING);
    std::int32_t currentAccessPointId  = 0;
    if (logChannel) {
        currentAccessPointId = logChannel->getServer()->getAccessPointId();
    }
    TimeoutInfo timeoutInfo(currentAccessPointId,
            clock_t::now() + std::chrono::seconds(uploadStrategy_->getTimeout()));

    timeouts_.insert(std::make_pair(requestId, timeoutInfo));
}

bool LogCollector::removeDeliveryTimeout(std::int32_t requestId)
{
    KAA_MUTEX_LOCKING("timeoutsGuard_");
    KAA_MUTEX_UNIQUE_DECLARE(timeoutsLock, timeoutsGuard_);
    KAA_MUTEX_LOCKED("timeoutsGuard_");

    return timeouts_.erase(requestId);
}

std::shared_ptr<LogSyncRequest> LogCollector::getLogUploadRequest()
{
    ILogStorage::RecordPack recordPack;
    std::shared_ptr<LogSyncRequest> request;

    {
        KAA_MUTEX_LOCKING("storageGuard_");
        KAA_MUTEX_UNIQUE_DECLARE(lock, storageGuard_);
        KAA_MUTEX_LOCKED("storageGuard_");

        recordPack = storage_->getRecordBlock(uploadStrategy_->getBatchSize(), uploadStrategy_->getRecordsBatchCount());
    }

    if (!recordPack.second.empty()) {
        request.reset(new LogSyncRequest);
        request->requestId = recordPack.first;

        std::vector<LogEntry> logs;
        logs.reserve(recordPack.second.size());
        for (const auto& log : recordPack.second) {
            logs.push_back(log->getLogEntry());
        }

        request->logEntries.set_array(std::move(logs));
        addDeliveryTimeout(request->requestId);
    }

    return request;
}

void LogCollector::onLogUploadResponse(const LogSyncResponse& response)
{
    KAA_MUTEX_LOCKING("storageGuard_");
    KAA_MUTEX_UNIQUE_DECLARE(storageLock, storageGuard_);
    KAA_MUTEX_LOCKED("storageGuard_");

    if (!response.deliveryStatuses.is_null()) {
        const auto& deliveryStatuses = response.deliveryStatuses.get_array();
        for (const auto& status : deliveryStatuses) {
            if (!removeDeliveryTimeout(status.requestId)) {
                continue;
            }

            if (status.result == SyncResponseResultType::SUCCESS) {
                KAA_LOG_INFO(boost::format("Logs (requestId %ld) successfully delivered") % status.requestId);
                storage_->removeRecordBlock(status.requestId);
            } else {
                storage_->notifyUploadFailed(status.requestId);

                KAA_MUTEX_UNLOCKING("storageGuard_");
                KAA_UNLOCK(storageLock);
                KAA_MUTEX_UNLOCKED("storageGuard_");

                if (!status.errorCode.is_null()) {
                    KAA_LOG_WARN(boost::format("Logs (requestId %ld) failed to deliver (error %d)")
                                            % status.requestId % (int)status.errorCode.get_LogDeliveryErrorCode());
                    uploadStrategy_->onFailure(*this, status.errorCode.get_LogDeliveryErrorCode());
                } else {
                    KAA_LOG_WARN("Log delivery failed, but no error code received");
                }

                KAA_MUTEX_LOCKING("storageGuard_");
                KAA_LOCK(storageLock);
                KAA_MUTEX_LOCKED("storageGuard_");
            }
        }
    }

    KAA_MUTEX_UNLOCKING("storageGuard_");
    KAA_UNLOCK(storageLock);
    KAA_MUTEX_UNLOCKED("storageGuard_");

    processLogUploadDecision(uploadStrategy_->isUploadNeeded(storage_->getStatus()));
}

void LogCollector::setTransport(LoggingTransport* transport)
{
    KAA_MUTEX_LOCKING("transportGuard_");
    KAA_MUTEX_UNIQUE_DECLARE(transportLock, transportGuard_);
    KAA_MUTEX_LOCKED("transportGuard_");

    transport_ = transport;
}

void LogCollector::retryLogUpload()
{
    KAA_LOG_INFO("Going to retry log upload...");
    doSync();
}

void LogCollector::retryLogUpload(std::size_t delay)
{
    KAA_LOG_INFO(boost::format("Schedule log upload with %u second(s) delay ...") % delay);
    uploadTimer_.stop();
    uploadTimer_.start(delay, [&] { doSync(); });
}

void LogCollector::switchAccessPoint()
{
    IDataChannelPtr logChannel = channelManager_->getChannelByTransportType(TransportType::LOGGING);
    if (logChannel && logChannel->getServer()) {
        if (timeoutAccessPointId_ == logChannel->getServer()->getAccessPointId()) {
            KAA_LOG_WARN("Try to switch to another Operations server...");
            channelManager_->onServerFailed(logChannel->getServer());
        }
    } else {
        KAA_LOG_ERROR("Can't find LOGGING data channel");
    }
}

}  // namespace kaa

<|endoftext|>