<commit_before>#include "MainFrame.h"

#include "RGBModel.h"
#include "HSLModel.h"
#include "CMYKModel.h"

#include "HtmlHexOutput.h"
#include "CssRgbOutput.h"

#include <wx/aboutdlg.h>
#include <wx/dcscreen.h>
#include <wx/graphics.h>
#include <wx/dcmemory.h>
#include <wx/dialog.h>
#include <wx/gdicmn.h>
#include <wx/dnd.h>
#include <wx/dataobj.h>
#include <wx/colordlg.h>
#include <wx/colourdata.h>

struct ZoomMenuFunctor
{
    ZoomPanel* panel;
    int zoom;
    ZoomMenuFunctor(ZoomPanel* p, int z) : panel(p), zoom(z) { }
    void operator()(wxCommandEvent& event)
    {
        panel->SetZoom(zoom);
    }
};

MainFrame::MainFrame(wxWindow* parent)
    : MainFrameBaseClass(parent), capturing(false), refreshTimer(this)
{
    Bind(wxEVT_TIMER, &MainFrame::OnRefreshTimerEvent, this, refreshTimer.GetId());
    RestorePosition();

    colorOutput = new HtmlHexOutput;
    AddColorOutput(colorOutput);
    AddColorOutput(new CssRgbOutput);

    colorModel = new RGBModel;
    AddColorModel(colorModel);
    AddColorModel(new HSLModel);
    AddColorModel(new CMYKModel);
    UpdateColorModel();

    SetColor(wxColour(
        config.ReadLong("Main/Color/R", 0),
        config.ReadLong("Main/Color/G", 0),
        config.ReadLong("Main/Color/B", 0)
    ));

    m_zoomPanel->SetPoi(wxPoint(
        config.ReadLong("Main/ZoomPanel/X", 0),
        config.ReadLong("Main/ZoomPanel/Y", 0)
    ));
    m_zoomPanel->SetZoom(config.ReadLong("Main/ZoomPanel/Zoom", 4));
}

MainFrame::~MainFrame()
{
    wxPoint pos = GetPosition();
    config.Write("Main/Position/X", pos.x);
    config.Write("Main/Position/Y", pos.y);
    wxColour col = GetColor();
    config.Write("Main/Color/R", col.Red());
    config.Write("Main/Color/G", col.Green());
    config.Write("Main/Color/B", col.Blue());
    wxPoint poi = m_zoomPanel->GetPoi();
    config.Write("Main/ZoomPanel/X", poi.x);
    config.Write("Main/ZoomPanel/Y", poi.y);
    config.Write("Main/ZoomPanel/Zoom", m_zoomPanel->GetZoom());
    config.Write("Main/Model", wxString(colorModel->getName()));
    config.Write("Main/Output", wxString(colorOutput->getName()));
}

void MainFrame::RestorePosition()
{
    int x = config.ReadLong("Main/Position/X", GetPosition().x);
    int y = config.ReadLong("Main/Position/Y", GetPosition().y);
    wxSize screenSize = wxGetDisplaySize();
    wxSize windowSize = GetSize();
    if (x < 0) x = 0;
    if (y < 0) y = 0;
    if (x > (screenSize.x - windowSize.x)) x = screenSize.x - windowSize.x;
    if (y > (screenSize.y - windowSize.y)) y = screenSize.y - windowSize.y;
    SetPosition(wxPoint(x, y));
}

int MainFrame::AddColorModel(IColorModel* colorModel)
{
    wxWindowID id = wxIdManager::ReserveId();
    colorModels[id] = colorModel;
    wxMenuItem* menuItem = new wxMenuItem(m_colorModelMenu, id, colorModel->getName(), wxT(""), wxITEM_RADIO);
    m_colorModelMenu->Append(menuItem);
    m_colorModelMenu->Bind(wxEVT_COMMAND_MENU_SELECTED, &MainFrame::OnSelectColorModel, this, menuItem->GetId());
    if (colorModel->getName() == config.Read("Main/Model", "RGB"))
    {
        this->colorModel = colorModel;
        menuItem->Check();
    }
    return id;
}

int MainFrame::AddColorOutput(IColorOutput* colorOutput)
{
    wxWindowID id = wxIdManager::ReserveId();
    colorOutputs[id] = colorOutput;
    wxMenuItem* menuItem = new wxMenuItem(m_colorOutputMenu, id, colorOutput->getName(), wxT(""), wxITEM_RADIO);
    m_colorOutputMenu->Append(menuItem);
    m_colorOutputMenu->Bind(wxEVT_COMMAND_MENU_SELECTED, &MainFrame::OnSelectColorOutput, this, menuItem->GetId());
    if (colorOutput->getName() == config.Read("Main/Output", "Hexadecimal (HTML/CSS)"))
    {
        this->colorOutput = colorOutput;
        menuItem->Check();
    }
    return id;
}

void MainFrame::SetColorModel(IColorModel* colorModel)
{
    this->colorModel = colorModel;
    UpdateColorModel();
}

void MainFrame::SetColorOutput(IColorOutput* colorOutput)
{
    this->colorOutput = colorOutput;
    SetColor(GetColor());
}

void update_label_and_ctrl(int i, IColorModel* colorModel, wxStaticText* label, wxSpinCtrl* ctrl)
{
    if (colorModel->getNumComponents() > i) {
        label->Show(true);
        ctrl->Show(true);
        std::string labelStr = colorModel->getLabel(i);
        label->SetLabel(labelStr.substr(0, 1).append(":"));
        label->SetToolTip(labelStr);
        ctrl->SetToolTip(labelStr);
        ctrl->SetMin(colorModel->getMin(i));
        ctrl->SetMax(colorModel->getMax(i));
    }
    else {
        label->Show(false);
        ctrl->Show(false);
    }
}

void MainFrame::UpdateColorModel()
{
    update_label_and_ctrl(0, colorModel, m_firstLabel, m_firstCtrl);
    update_label_and_ctrl(1, colorModel, m_secondLabel, m_secondCtrl);
    update_label_and_ctrl(2, colorModel, m_thirdLabel, m_thirdCtrl);
    update_label_and_ctrl(3, colorModel, m_fourthLabel, m_fourthCtrl);
    SetColor(GetColor());
}

void MainFrame::OnExit(wxCommandEvent& event)
{
    wxUnusedVar(event);
    Close();
}

void MainFrame::OnAbout(wxCommandEvent& event)
{
    wxUnusedVar(event);
    wxAboutDialogInfo info;
    info.SetName("ColorGrab");
    info.SetVersion("0.1-dev");
    info.SetWebSite("http://nielssp.dk");
    info.SetCopyright(_("(C) 2015 Niels Sonnich Poulsen"));
    info.SetLicence(_("MIT License"));
    info.SetDescription(_("Free color picker tool."));
    ::wxAboutBox(info);
}

wxBitmap GetScreenShot()
{
    wxSize screenSize = wxGetDisplaySize();
    wxBitmap bitmap(screenSize.x, screenSize.y);
    wxScreenDC dc;
    wxMemoryDC memDC;
    memDC.SelectObject(bitmap);
    memDC.Blit(0, 0, screenSize.x, screenSize.y, &dc, 0, 0);
    memDC.SelectObject(wxNullBitmap);
    return bitmap;
}

void MainFrame::UpdateZoomArea()
{
    m_zoomPanel->SetPoi(wxGetMousePosition());
}

void MainFrame::SetColorFromMouse()
{
    int x, y;
    wxGetMousePosition(&x, &y);
    SetColorFromPixel(x, y);
}

void MainFrame::SetColorFromPixel(wxCoord x, wxCoord y)
{
    wxColor color;
    wxScreenDC dc;
    dc.GetPixel(x, y, &color);
    SetColor(color);
}

wxColor MainFrame::GetColor() const
{
    return m_colorButton->GetBackgroundColour();
}

void MainFrame::SetColor(const wxColor& color, bool updateInputs, bool updateOutput)
{
//    m_colourPicker->SetColour(color);
    m_colorButton->SetBackgroundColour(color);
	m_colorButton->Refresh();
    colorModel->setColor(color);
    if (updateInputs)
    {
        m_firstCtrl->SetValue(colorModel->getValue(0));
        m_secondCtrl->SetValue(colorModel->getValue(1));
        m_thirdCtrl->SetValue(colorModel->getValue(2));
        m_fourthCtrl->SetValue(colorModel->getValue(3));
    }
    colorOutput->setColor(color);
    if (updateOutput)
        m_formatText->ChangeValue(colorOutput->getOutput());
}

void MainFrame::OnColorChange(wxCommandEvent& event)
{
    colorModel->setValue(0, m_firstCtrl->GetValue());
    colorModel->setValue(1, m_secondCtrl->GetValue());
    colorModel->setValue(2, m_thirdCtrl->GetValue());
    colorModel->setValue(3, m_fourthCtrl->GetValue());
    SetColor(colorModel->getColor(), false);
}

void MainFrame::OnCaptureStart(wxMouseEvent& event)
{
    SetColorFromMouse();
    UpdateZoomArea();
    CaptureMouse();
    SetCursor(*wxCROSS_CURSOR);
    capturing = true;
    SetFocus();
}
void MainFrame::OnCaptureEnd(wxMouseEvent& event)
{
    if (capturing)
    {
        SetColorFromMouse();
        UpdateZoomArea();
        ReleaseMouse();
        capturing = false;
        SetCursor(wxNullCursor);
    }
}
void MainFrame::OnCaptureMove(wxMouseEvent& event)
{
    if (capturing) {
        SetColorFromMouse();
        UpdateZoomArea();
    }
}

void MainFrame::OnSystemColorPicker(wxCommandEvent& event)
{
    wxColourDialog dialog(this);
    dialog.GetColourData().SetColour(GetColor());
    dialog.ShowModal();
    SetColor(dialog.GetColourData().GetColour());
}
void MainFrame::OnFormatChoose(wxMenuEvent& event)
{
}
void MainFrame::OnFormatClick(wxCommandEvent& event)
{
}
void MainFrame::OnCaptureZoom(wxMouseEvent& event)
{
    if (capturing)
        OnZoomPanelZoom(event);
}

void MainFrame::OnZoomSelect(wxCommandEvent& event)
{
    m_zoomPanel->SetZoom(2);
}
void MainFrame::OnZoomPanelDown(wxMouseEvent& event)
{
    m_zoomPanel->ShowPoi(false);
    m_zoomPanel->SetFocus();
    SetColorFromMouse();
    capturing = true;
}
void MainFrame::OnZoomPanelMove(wxMouseEvent& event)
{
    m_zoomPanel->SetFocus();
    if (capturing)
        SetColorFromMouse();
}
void MainFrame::OnZoomPanelUp(wxMouseEvent& event)
{
    SetColorFromMouse();
    capturing = false;
}
void MainFrame::OnZoomPanelZoom(wxMouseEvent& event)
{
    int zoom = m_zoomPanel->GetZoom();
    if (event.GetWheelRotation() > 0 && zoom < 64)
        zoom *= 2;
    else if (event.GetWheelRotation() < 0 && zoom > 1)
        zoom /= 2;
    m_zoomPanel->SetZoom(zoom);
}

void MainFrame::OnSelectColorModel(wxCommandEvent& event)
{
    SetColorModel(colorModels[event.GetId()]);
}

void MainFrame::OnSelectColorOutput(wxCommandEvent& event)
{
    SetColorOutput(colorOutputs[event.GetId()]);
}
void MainFrame::OnColorOutputChange(wxCommandEvent& event)
{
    colorOutput->setOutput(m_formatText->GetValue().ToStdString());
    SetColor(colorOutput->getColor(), true, false);
}
void MainFrame::OnInputOutputBlur(wxFocusEvent& event)
{
    SetColor(GetColor());
    event.Skip();
}
void MainFrame::OnInputOutputEnter(wxCommandEvent& event)
{
    SetColor(GetColor());
}
void MainFrame::OnZoomIn(wxCommandEvent& event)
{
    int zoom = m_zoomPanel->GetZoom();
    if (zoom < 64)
        zoom *= 2;
    m_zoomPanel->SetZoom(zoom);
}
void MainFrame::OnZoomOut(wxCommandEvent& event)
{
    int zoom = m_zoomPanel->GetZoom();
    if (zoom > 1)
        zoom /= 2;
    m_zoomPanel->SetZoom(zoom);
}
void MainFrame::OnRefreshImage(wxCommandEvent& event)
{
    m_zoomPanel->Update();
}
void MainFrame::OnSelectTiming(wxCommandEvent& event)
{
    int interval = timerIntervals[event.GetId()];
    m_timerButton->Disable();
    refreshTimer.Start(interval * 1000);
}
void MainFrame::OnTimerRefreshImage(wxCommandEvent& event)
{
    wxMenu menu(_("Refresh image in..."));
    timerIntervals.clear();
    int intervals [] = {1, 2, 5};
    for (int i = 0; i < 3; i++) {
        wxMenuItem* item;
        if (i == 1)
            item = menu.Append(wxID_ANY, wxString::Format(_("%d second"), intervals[i]));
        else
            item = menu.Append(wxID_ANY, wxString::Format(_("%d seconds"), intervals[i]));
        menu.Bind(wxEVT_COMMAND_MENU_SELECTED, &MainFrame::OnSelectTiming, this, item->GetId());
        timerIntervals[item->GetId()] = intervals[i];
    }
    m_timerButton->PopupMenu(&menu);
}

void MainFrame::OnRefreshTimerEvent(wxTimerEvent& event)
{
    m_zoomPanel->Update();
    m_timerButton->Enable();
    refreshTimer.Stop();
}<commit_msg>Model/output shortcuts.<commit_after>#include "MainFrame.h"

#include "RGBModel.h"
#include "HSLModel.h"
#include "CMYKModel.h"

#include "HtmlHexOutput.h"
#include "CssRgbOutput.h"

#include <wx/aboutdlg.h>
#include <wx/dcscreen.h>
#include <wx/graphics.h>
#include <wx/dcmemory.h>
#include <wx/dialog.h>
#include <wx/gdicmn.h>
#include <wx/dnd.h>
#include <wx/dataobj.h>
#include <wx/colordlg.h>
#include <wx/colourdata.h>

struct ZoomMenuFunctor
{
    ZoomPanel* panel;
    int zoom;
    ZoomMenuFunctor(ZoomPanel* p, int z) : panel(p), zoom(z) { }
    void operator()(wxCommandEvent& event)
    {
        panel->SetZoom(zoom);
    }
};

MainFrame::MainFrame(wxWindow* parent)
    : MainFrameBaseClass(parent), capturing(false), refreshTimer(this)
{
    Bind(wxEVT_TIMER, &MainFrame::OnRefreshTimerEvent, this, refreshTimer.GetId());
    RestorePosition();

    colorOutput = new HtmlHexOutput;
    AddColorOutput(colorOutput);
    AddColorOutput(new CssRgbOutput);

    colorModel = new RGBModel;
    AddColorModel(colorModel);
    AddColorModel(new HSLModel);
    AddColorModel(new CMYKModel);
    UpdateColorModel();

    SetColor(wxColour(
        config.ReadLong("Main/Color/R", 0),
        config.ReadLong("Main/Color/G", 0),
        config.ReadLong("Main/Color/B", 0)
    ));

    m_zoomPanel->SetPoi(wxPoint(
        config.ReadLong("Main/ZoomPanel/X", 0),
        config.ReadLong("Main/ZoomPanel/Y", 0)
    ));
    m_zoomPanel->SetZoom(config.ReadLong("Main/ZoomPanel/Zoom", 4));
}

MainFrame::~MainFrame()
{
    wxPoint pos = GetPosition();
    config.Write("Main/Position/X", pos.x);
    config.Write("Main/Position/Y", pos.y);
    wxColour col = GetColor();
    config.Write("Main/Color/R", col.Red());
    config.Write("Main/Color/G", col.Green());
    config.Write("Main/Color/B", col.Blue());
    wxPoint poi = m_zoomPanel->GetPoi();
    config.Write("Main/ZoomPanel/X", poi.x);
    config.Write("Main/ZoomPanel/Y", poi.y);
    config.Write("Main/ZoomPanel/Zoom", m_zoomPanel->GetZoom());
    config.Write("Main/Model", wxString(colorModel->getName()));
    config.Write("Main/Output", wxString(colorOutput->getName()));
}

void MainFrame::RestorePosition()
{
    int x = config.ReadLong("Main/Position/X", GetPosition().x);
    int y = config.ReadLong("Main/Position/Y", GetPosition().y);
    wxSize screenSize = wxGetDisplaySize();
    wxSize windowSize = GetSize();
    if (x < 0) x = 0;
    if (y < 0) y = 0;
    if (x > (screenSize.x - windowSize.x)) x = screenSize.x - windowSize.x;
    if (y > (screenSize.y - windowSize.y)) y = screenSize.y - windowSize.y;
    SetPosition(wxPoint(x, y));
}

int MainFrame::AddColorModel(IColorModel* colorModel)
{
    wxWindowID id = wxIdManager::ReserveId();
    colorModels[id] = colorModel;
    wxMenuItem* menuItem = new wxMenuItem(m_colorModelMenu, id, wxString::Format("%s\tCtrl-%d", colorModel->getName(), colorModels.size()), wxT(""), wxITEM_RADIO);
    m_colorModelMenu->Append(menuItem);
    m_colorModelMenu->Bind(wxEVT_COMMAND_MENU_SELECTED, &MainFrame::OnSelectColorModel, this, menuItem->GetId());
    if (colorModel->getName() == config.Read("Main/Model", "RGB"))
    {
        this->colorModel = colorModel;
        menuItem->Check();
    }
    return id;
}

int MainFrame::AddColorOutput(IColorOutput* colorOutput)
{
    wxWindowID id = wxIdManager::ReserveId();
    colorOutputs[id] = colorOutput;
    wxMenuItem* menuItem = new wxMenuItem(m_colorOutputMenu, id, wxString::Format("%s\tCtrl-Shift-%d", colorOutput->getName(), colorOutputs.size()), wxT(""), wxITEM_RADIO);
    m_colorOutputMenu->Append(menuItem);
    m_colorOutputMenu->Bind(wxEVT_COMMAND_MENU_SELECTED, &MainFrame::OnSelectColorOutput, this, menuItem->GetId());
    if (colorOutput->getName() == config.Read("Main/Output", "Hexadecimal (HTML/CSS)"))
    {
        this->colorOutput = colorOutput;
        menuItem->Check();
    }
    return id;
}

void MainFrame::SetColorModel(IColorModel* colorModel)
{
    this->colorModel = colorModel;
    UpdateColorModel();
}

void MainFrame::SetColorOutput(IColorOutput* colorOutput)
{
    this->colorOutput = colorOutput;
    SetColor(GetColor());
}

void update_label_and_ctrl(int i, IColorModel* colorModel, wxStaticText* label, wxSpinCtrl* ctrl)
{
    if (colorModel->getNumComponents() > i) {
        label->Show(true);
        ctrl->Show(true);
        std::string labelStr = colorModel->getLabel(i);
        label->SetLabel(labelStr.substr(0, 1).append(":"));
        label->SetToolTip(labelStr);
        ctrl->SetToolTip(labelStr);
        ctrl->SetMin(colorModel->getMin(i));
        ctrl->SetMax(colorModel->getMax(i));
    }
    else {
        label->Show(false);
        ctrl->Show(false);
    }
}

void MainFrame::UpdateColorModel()
{
    update_label_and_ctrl(0, colorModel, m_firstLabel, m_firstCtrl);
    update_label_and_ctrl(1, colorModel, m_secondLabel, m_secondCtrl);
    update_label_and_ctrl(2, colorModel, m_thirdLabel, m_thirdCtrl);
    update_label_and_ctrl(3, colorModel, m_fourthLabel, m_fourthCtrl);
    SetColor(GetColor());
}

void MainFrame::OnExit(wxCommandEvent& event)
{
    wxUnusedVar(event);
    Close();
}

void MainFrame::OnAbout(wxCommandEvent& event)
{
    wxUnusedVar(event);
    wxAboutDialogInfo info;
    info.SetName("ColorGrab");
    info.SetVersion("0.1-dev");
    info.SetWebSite("http://nielssp.dk");
    info.SetCopyright(_("(C) 2015 Niels Sonnich Poulsen"));
    info.SetLicence(_("MIT License"));
    info.SetDescription(_("Free color picker tool."));
    ::wxAboutBox(info);
}

wxBitmap GetScreenShot()
{
    wxSize screenSize = wxGetDisplaySize();
    wxBitmap bitmap(screenSize.x, screenSize.y);
    wxScreenDC dc;
    wxMemoryDC memDC;
    memDC.SelectObject(bitmap);
    memDC.Blit(0, 0, screenSize.x, screenSize.y, &dc, 0, 0);
    memDC.SelectObject(wxNullBitmap);
    return bitmap;
}

void MainFrame::UpdateZoomArea()
{
    m_zoomPanel->SetPoi(wxGetMousePosition());
}

void MainFrame::SetColorFromMouse()
{
    int x, y;
    wxGetMousePosition(&x, &y);
    SetColorFromPixel(x, y);
}

void MainFrame::SetColorFromPixel(wxCoord x, wxCoord y)
{
    wxColor color;
    wxScreenDC dc;
    dc.GetPixel(x, y, &color);
    SetColor(color);
}

wxColor MainFrame::GetColor() const
{
    return m_colorButton->GetBackgroundColour();
}

void MainFrame::SetColor(const wxColor& color, bool updateInputs, bool updateOutput)
{
//    m_colourPicker->SetColour(color);
    m_colorButton->SetBackgroundColour(color);
	m_colorButton->Refresh();
    colorModel->setColor(color);
    if (updateInputs)
    {
        m_firstCtrl->SetValue(colorModel->getValue(0));
        m_secondCtrl->SetValue(colorModel->getValue(1));
        m_thirdCtrl->SetValue(colorModel->getValue(2));
        m_fourthCtrl->SetValue(colorModel->getValue(3));
    }
    colorOutput->setColor(color);
    if (updateOutput)
        m_formatText->ChangeValue(colorOutput->getOutput());
}

void MainFrame::OnColorChange(wxCommandEvent& event)
{
    colorModel->setValue(0, m_firstCtrl->GetValue());
    colorModel->setValue(1, m_secondCtrl->GetValue());
    colorModel->setValue(2, m_thirdCtrl->GetValue());
    colorModel->setValue(3, m_fourthCtrl->GetValue());
    SetColor(colorModel->getColor(), false);
}

void MainFrame::OnCaptureStart(wxMouseEvent& event)
{
    SetColorFromMouse();
    UpdateZoomArea();
    CaptureMouse();
    SetCursor(*wxCROSS_CURSOR);
    capturing = true;
    SetFocus();
}
void MainFrame::OnCaptureEnd(wxMouseEvent& event)
{
    if (capturing)
    {
        SetColorFromMouse();
        UpdateZoomArea();
        ReleaseMouse();
        capturing = false;
        SetCursor(wxNullCursor);
    }
}
void MainFrame::OnCaptureMove(wxMouseEvent& event)
{
    if (capturing) {
        SetColorFromMouse();
        UpdateZoomArea();
    }
}

void MainFrame::OnSystemColorPicker(wxCommandEvent& event)
{
    wxColourDialog dialog(this);
    dialog.GetColourData().SetColour(GetColor());
    dialog.ShowModal();
    SetColor(dialog.GetColourData().GetColour());
}
void MainFrame::OnFormatChoose(wxMenuEvent& event)
{
}
void MainFrame::OnFormatClick(wxCommandEvent& event)
{
}
void MainFrame::OnCaptureZoom(wxMouseEvent& event)
{
    if (capturing)
        OnZoomPanelZoom(event);
}

void MainFrame::OnZoomSelect(wxCommandEvent& event)
{
    m_zoomPanel->SetZoom(2);
}
void MainFrame::OnZoomPanelDown(wxMouseEvent& event)
{
    m_zoomPanel->ShowPoi(false);
    m_zoomPanel->SetFocus();
    SetColorFromMouse();
    capturing = true;
}
void MainFrame::OnZoomPanelMove(wxMouseEvent& event)
{
    m_zoomPanel->SetFocus();
    if (capturing)
        SetColorFromMouse();
}
void MainFrame::OnZoomPanelUp(wxMouseEvent& event)
{
    SetColorFromMouse();
    capturing = false;
}
void MainFrame::OnZoomPanelZoom(wxMouseEvent& event)
{
    int zoom = m_zoomPanel->GetZoom();
    if (event.GetWheelRotation() > 0 && zoom < 64)
        zoom *= 2;
    else if (event.GetWheelRotation() < 0 && zoom > 1)
        zoom /= 2;
    m_zoomPanel->SetZoom(zoom);
}

void MainFrame::OnSelectColorModel(wxCommandEvent& event)
{
    SetColorModel(colorModels[event.GetId()]);
}

void MainFrame::OnSelectColorOutput(wxCommandEvent& event)
{
    SetColorOutput(colorOutputs[event.GetId()]);
}
void MainFrame::OnColorOutputChange(wxCommandEvent& event)
{
    colorOutput->setOutput(m_formatText->GetValue().ToStdString());
    SetColor(colorOutput->getColor(), true, false);
}
void MainFrame::OnInputOutputBlur(wxFocusEvent& event)
{
    SetColor(GetColor());
    event.Skip();
}
void MainFrame::OnInputOutputEnter(wxCommandEvent& event)
{
    SetColor(GetColor());
}
void MainFrame::OnZoomIn(wxCommandEvent& event)
{
    int zoom = m_zoomPanel->GetZoom();
    if (zoom < 64)
        zoom *= 2;
    m_zoomPanel->SetZoom(zoom);
}
void MainFrame::OnZoomOut(wxCommandEvent& event)
{
    int zoom = m_zoomPanel->GetZoom();
    if (zoom > 1)
        zoom /= 2;
    m_zoomPanel->SetZoom(zoom);
}
void MainFrame::OnRefreshImage(wxCommandEvent& event)
{
    m_zoomPanel->Update();
}
void MainFrame::OnSelectTiming(wxCommandEvent& event)
{
    int interval = timerIntervals[event.GetId()];
    m_timerButton->Disable();
    refreshTimer.Start(interval * 1000);
}
void MainFrame::OnTimerRefreshImage(wxCommandEvent& event)
{
    wxMenu menu(_("Refresh image in..."));
    timerIntervals.clear();
    int intervals [] = {1, 2, 5};
    for (int i = 0; i < 3; i++) {
        wxMenuItem* item;
        if (i == 1)
            item = menu.Append(wxID_ANY, wxString::Format(_("%d second"), intervals[i]));
        else
            item = menu.Append(wxID_ANY, wxString::Format(_("%d seconds"), intervals[i]));
        menu.Bind(wxEVT_COMMAND_MENU_SELECTED, &MainFrame::OnSelectTiming, this, item->GetId());
        timerIntervals[item->GetId()] = intervals[i];
    }
    m_timerButton->PopupMenu(&menu);
}

void MainFrame::OnRefreshTimerEvent(wxTimerEvent& event)
{
    m_zoomPanel->Update();
    m_timerButton->Enable();
    refreshTimer.Stop();
}<|endoftext|>


<commit_before>//  Natron
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
*Created by Alexandre GAUTHIER-FOICHAT on 6/1/2012.
*contact: immarespond at gmail dot com
*
*/

#include "Gui/ComboBox.h"

#include <cassert>
#include <algorithm>
#include <QLayout>
#include <QLabel>
#include <QStyle>
#include <QFont>
#include <QFontMetrics>
#include <QTextDocument> // for Qt::convertFromPlainText

#include "Engine/AppManager.h"
#include "Gui/MenuWithToolTips.h"

using namespace Natron;

ComboBox::ComboBox(QWidget* parent)
: QFrame(parent)
, _currentIndex(0)
#if IS_MAXIMUMTEXTSIZE_USEFUL
, _maximumTextSize(0)
#endif
, pressed(false)
, animation(0)
{

    _mainLayout = new QHBoxLayout(this);
    _mainLayout->setSpacing(0);
    _mainLayout->setContentsMargins(0, 0, 0, 0);
    setLayout(_mainLayout);
    setFrameShape(QFrame::Box);

    _currentText = new QLabel(this);
    _currentText->setObjectName("ComboBoxLabel");
    setCurrentIndex(-1);
    _currentText->setMinimumWidth(10);
    _mainLayout->addWidget(_currentText);
    _currentText->setFixedHeight(fontMetrics().height() + 8);

    _dropDownIcon = new QLabel(this);
    _dropDownIcon->setObjectName("ComboBoxDropDownLabel");
    QPixmap pixC;
    appPTR->getIcon(NATRON_PIXMAP_COMBOBOX, &pixC);
    _dropDownIcon->setPixmap(pixC);
    _mainLayout->addWidget(_dropDownIcon);

    _menu = new MenuWithToolTips(this);

    setSizePolicy(QSizePolicy::Preferred,QSizePolicy::Fixed);
}

void ComboBox::paintEvent(QPaintEvent *e)
{
//    QStyleOption opt;
//    opt.init(this);
//    QPainter p(this);
//    style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
    QFrame::paintEvent(e);
}

void ComboBox::mousePressEvent(QMouseEvent* e){
    QPixmap pixC;
    appPTR->getIcon(NATRON_PIXMAP_COMBOBOX_PRESSED, &pixC);
    _dropDownIcon->setPixmap(pixC);
    setPressed(true);
    style()->unpolish(this);
    style()->polish(this);
    createMenu();
    QFrame::mousePressEvent(e);
}

void ComboBox::mouseReleaseEvent(QMouseEvent* e){
    QPixmap pixC;
    appPTR->getIcon(NATRON_PIXMAP_COMBOBOX, &pixC);
    _dropDownIcon->setPixmap(pixC);
    setPressed(false);
    style()->unpolish(this);
    style()->polish(this);
    QFrame::mouseReleaseEvent(e);
}

void ComboBox::createMenu(){
    _menu->clear();
    for (U32 i = 0 ; i < _actions.size(); ++i) {
        for (U32 j = 0; j < _separators.size(); ++j) {
            if (_separators[j] == (int)i) {
                _menu->addSeparator();
                break;
            }
        }
        _menu->addAction(_actions[i]);
    }
    QAction* triggered = _menu->exec(this->mapToGlobal(QPoint(0,height())));
    for (U32 i = 0; i < _actions.size(); ++i) {
        if (triggered == _actions[i]) {
            setCurrentIndex(i);
            break;
        }
    }

    QPixmap pixC;
    appPTR->getIcon(NATRON_PIXMAP_COMBOBOX, &pixC);
    _dropDownIcon->setPixmap(pixC);
    setPressed(false);
    style()->unpolish(this);
    style()->polish(this);

}
int ComboBox::count() const{
    return (int)_actions.size();
}
void ComboBox::insertItem(int index,const QString& item,QIcon icon,QKeySequence key,const QString& toolTip){
    assert(index >= 0);
    QAction* action =  new QAction(this);
    action->setText(item);
    if(!toolTip.isEmpty()){
        action->setToolTip(Qt::convertFromPlainText(toolTip, Qt::WhiteSpaceNormal));
    }
    if (!icon.isNull()) {
        action->setIcon(icon);
    }
    if (!key.isEmpty()) {
        action->setShortcut(key);
    }
#if IS_MAXIMUMTEXTSIZE_USEFUL
    if (item.size() > _maximumTextSize) {
        _maximumTextSize = item.size();
    }
#endif
    growMaximumWidthFromText(item);
    _actions.insert(_actions.begin()+index, action);
    /*if this is the first action we add, make it current*/
    if(_actions.size() == 1){
        setCurrentText(itemText(0));
    }

}

void ComboBox::addItem(const QString& item,QIcon icon ,QKeySequence key,const QString& toolTip){
    QAction* action =  new QAction(this);

    action->setText(item);
    if (!icon.isNull()) {
        action->setIcon(icon);
    }
    if (!key.isEmpty()) {
        action->setShortcut(key);
    }
    if(!toolTip.isEmpty()){
        action->setToolTip(Qt::convertFromPlainText(toolTip, Qt::WhiteSpaceNormal));
    }
#if IS_MAXIMUMTEXTSIZE_USEFUL
    if (item.size() > _maximumTextSize) {
        _maximumTextSize = item.size();
    }
#endif
    growMaximumWidthFromText(item);
    _actions.push_back(action);

    /*if this is the first action we add, make it current*/
    if(_actions.size() == 1){
        setCurrentText(itemText(0));
    }
}

void ComboBox::setCurrentText(const QString& text){
    QString str(text);
    growMaximumWidthFromText(str);
    str.prepend("  ");
    str.append("  ");
    assert(_currentText);
    _currentText->setText(str);
    for (U32 i = 0; i < _actions.size(); ++i) {
        if(_actions[i]->text() == text){
            _currentIndex = i;
            break;
        }
    }
}

void ComboBox::setMaximumWidthFromText(const QString& str)
{
    int w = _currentText->fontMetrics().width(str+"    ");
    setMaximumWidth(w);
}

void ComboBox::growMaximumWidthFromText(const QString& str)
{
    int w = _currentText->fontMetrics().width(str+"    ");
    if (w > maximumWidth()) {
        setMaximumWidth(w);
    }
}

QString ComboBox::text() const{
    return _currentText->text();
}

int ComboBox::activeIndex() const{
    return _currentIndex;
}

QString ComboBox::getCurrentIndexText() const {
    assert(_currentIndex < (int)_actions.size());
    return _actions[_currentIndex]->text();
}

void ComboBox::setCurrentIndex(int index)
{
    QString str;
    QString rawStr;
    if (index >= 0 && index < (int)_actions.size()) {
        str = _actions[index]->text();
        rawStr = str;
    }
    /*before displaying,prepend and append the text by some spacing.
     This is a dirty way to do this but QLayout::addSpacing() doesn't preserve
     the same style for the label.*/
#if IS_MAXIMUMTEXTSIZE_USEFUL
    int dsize = _maximumTextSize - str.size();
    dsize/=2;
    str.prepend("  ");
    for (int i = 0; i < dsize ; ++i) {str.prepend(" ");}
    str.append("  ");
    for (int i = 0; i < dsize ; ++i) {str.append(" ");}
#endif
    str.prepend("  ");
    str.append("  ");
    _currentIndex = index;
    _currentText->setText(str);
    // already called growMaximumWidthFromText() from addItem() and insertItem()
    //setMaximumWidthFromText(str);
    emit currentIndexChanged(index);
    emit currentIndexChanged(rawStr);
}

void ComboBox::addSeparator(){
    _separators.push_back(_actions.size()-1);
}
void ComboBox::insertSeparator(int index){
    assert(index >= 0);
    _separators.push_back(index);
}

QString ComboBox::itemText(int index) const{
    if(0 <= index && index < (int)_actions.size()) {
        assert(_actions[index]);
        return _actions[index]->text();
    } else {
        return "";
    }
}
int ComboBox::itemIndex(const QString& str) const{
    for (U32 i = 0; i < _actions.size(); ++i) {
        if (_actions[i]->text() == str) {
            return i;
        }
    }
    return -1;
}

void ComboBox::removeItem(const QString& item){
    for (U32 i = 0; i < _actions.size(); ++i) {
        assert(_actions[i]);
        if (_actions[i]->text() == item) {
            _actions.erase(_actions.begin()+i);
            assert(_currentText);
            if (_currentText->text() == item) {
                setCurrentIndex(i-1);
            }
            /*adjust separators that were placed after this item*/
            for (U32 j = 0; j < _separators.size(); ++j) {
                if (_separators[j] >= (int)i) {
                    --_separators[j];
                }
            }
        }
    }
#if IS_MAXIMUMTEXTSIZE_USEFUL
    /*we also need to re-calculate the maximum text size*/
    _maximumTextSize = 0;
    for (U32 i = 0; i < _actions.size(); ++i) {
        assert(_actions[i]);
        if (_actions[i]->text().size() > _maximumTextSize) {
            _maximumTextSize = _actions[i]->text().size();
        }
    }
#endif
}

void ComboBox::clear(){
    _actions.clear();
    _menu->clear();
    _separators.clear();
    _currentIndex = 0;
#if IS_MAXIMUMTEXTSIZE_USEFUL
    _maximumTextSize = 0;
#endif
}

void ComboBox::setItemText(int index,const QString& item){
    assert(0 <= index && index < (int)_actions.size());
    assert(_actions[index]);
    _actions[index]->setText(item);
    growMaximumWidthFromText(item);
#if IS_MAXIMUMTEXTSIZE_USEFUL
    /*we also need to re-calculate the maximum text size*/
    _maximumTextSize = 0;
    for (U32 i = 0; i < _actions.size(); ++i) {
        assert(_actions[i]);
        if (_actions[i]->text().size() > _maximumTextSize) {
            _maximumTextSize = _actions[i]->text().size();
        }
    }
#endif
}

void ComboBox::setItemShortcut(int index,const QKeySequence& sequence){
    assert(0 <= index && index < (int)_actions.size());
    assert(_actions[index]);
    _actions[index]->setShortcut(sequence);
}

void ComboBox::setItemIcon(int index,const QIcon& icon){
    assert(0 <= index && index < (int)_actions.size());
    assert(_actions[index]);
    _actions[index]->setIcon(icon);
}
void ComboBox::disableItem(int index){
    assert(0 <= index && index < (int)_actions.size());
    assert(_actions[index]);
    _actions[index]->setEnabled(false);
}

void ComboBox::enableItem(int index){
    assert(0 <= index && index < (int)_actions.size());
    assert(_actions[index]);
    _actions[index]->setEnabled(true);
}

void ComboBox::setAnimation(int i){
    animation = i;
    style()->unpolish(this);
    style()->polish(this);
    repaint();
}
<commit_msg>ComboBox: add warnings, because strange things are happening here<commit_after>//  Natron
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
*Created by Alexandre GAUTHIER-FOICHAT on 6/1/2012.
*contact: immarespond at gmail dot com
*
*/

#include "Gui/ComboBox.h"

#include <cassert>
#include <algorithm>
#include <QLayout>
#include <QLabel>
#include <QStyle>
#include <QFont>
#include <QFontMetrics>
#include <QTextDocument> // for Qt::convertFromPlainText

#include "Engine/AppManager.h"
#include "Gui/MenuWithToolTips.h"

using namespace Natron;

ComboBox::ComboBox(QWidget* parent)
: QFrame(parent)
, _currentIndex(0)
#if IS_MAXIMUMTEXTSIZE_USEFUL
, _maximumTextSize(0)
#endif
, pressed(false)
, animation(0)
{

    _mainLayout = new QHBoxLayout(this);
    _mainLayout->setSpacing(0);
    _mainLayout->setContentsMargins(0, 0, 0, 0);
    setLayout(_mainLayout);
    setFrameShape(QFrame::Box);

    _currentText = new QLabel(this);
    _currentText->setObjectName("ComboBoxLabel");
    setCurrentIndex(-1);
    _currentText->setMinimumWidth(10);
    _mainLayout->addWidget(_currentText);
    _currentText->setFixedHeight(fontMetrics().height() + 8);

    _dropDownIcon = new QLabel(this);
    _dropDownIcon->setObjectName("ComboBoxDropDownLabel");
    QPixmap pixC;
    appPTR->getIcon(NATRON_PIXMAP_COMBOBOX, &pixC);
    _dropDownIcon->setPixmap(pixC);
    _mainLayout->addWidget(_dropDownIcon);

    _menu = new MenuWithToolTips(this);

    setSizePolicy(QSizePolicy::Preferred,QSizePolicy::Fixed);
}

void ComboBox::paintEvent(QPaintEvent *e)
{
//    QStyleOption opt;
//    opt.init(this);
//    QPainter p(this);
//    style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
    QFrame::paintEvent(e);
}

void ComboBox::mousePressEvent(QMouseEvent* e){
    QPixmap pixC;
    appPTR->getIcon(NATRON_PIXMAP_COMBOBOX_PRESSED, &pixC);
    _dropDownIcon->setPixmap(pixC);
    setPressed(true);
    style()->unpolish(this);
    style()->polish(this);
    createMenu();
    QFrame::mousePressEvent(e);
}

void ComboBox::mouseReleaseEvent(QMouseEvent* e){
    QPixmap pixC;
    appPTR->getIcon(NATRON_PIXMAP_COMBOBOX, &pixC);
    _dropDownIcon->setPixmap(pixC);
    setPressed(false);
    style()->unpolish(this);
    style()->polish(this);
    QFrame::mouseReleaseEvent(e);
}

void ComboBox::createMenu(){
    _menu->clear();
    for (U32 i = 0 ; i < _actions.size(); ++i) {
        for (U32 j = 0; j < _separators.size(); ++j) {
            if (_separators[j] == (int)i) {
                _menu->addSeparator();
                break;
            }
        }
        _menu->addAction(_actions[i]);
    }
    QAction* triggered = _menu->exec(this->mapToGlobal(QPoint(0,height())));
    for (U32 i = 0; i < _actions.size(); ++i) {
        if (triggered == _actions[i]) {
            setCurrentIndex(i);
            break;
        }
    }

    QPixmap pixC;
    appPTR->getIcon(NATRON_PIXMAP_COMBOBOX, &pixC);
    _dropDownIcon->setPixmap(pixC);
    setPressed(false);
    style()->unpolish(this);
    style()->polish(this);

}
int ComboBox::count() const{
    return (int)_actions.size();
}
void ComboBox::insertItem(int index,const QString& item,QIcon icon,QKeySequence key,const QString& toolTip){
    assert(index >= 0);
    QAction* action =  new QAction(this);
    action->setText(item);
    if(!toolTip.isEmpty()){
        action->setToolTip(Qt::convertFromPlainText(toolTip, Qt::WhiteSpaceNormal));
    }
    if (!icon.isNull()) {
        action->setIcon(icon);
    }
    if (!key.isEmpty()) {
        action->setShortcut(key);
    }
#if IS_MAXIMUMTEXTSIZE_USEFUL
    if (item.size() > _maximumTextSize) {
        _maximumTextSize = item.size();
    }
#endif
    growMaximumWidthFromText(item);
    _actions.insert(_actions.begin()+index, action);
    /*if this is the first action we add, make it current*/
    if(_actions.size() == 1){
        setCurrentText(itemText(0));
    }

}

void ComboBox::addItem(const QString& item,QIcon icon ,QKeySequence key,const QString& toolTip){
    QAction* action =  new QAction(this);

    action->setText(item);
    if (!icon.isNull()) {
        action->setIcon(icon);
    }
    if (!key.isEmpty()) {
        action->setShortcut(key);
    }
    if(!toolTip.isEmpty()){
        action->setToolTip(Qt::convertFromPlainText(toolTip, Qt::WhiteSpaceNormal));
    }
#if IS_MAXIMUMTEXTSIZE_USEFUL
    if (item.size() > _maximumTextSize) {
        _maximumTextSize = item.size();
    }
#endif
    growMaximumWidthFromText(item);
    _actions.push_back(action);

    /*if this is the first action we add, make it current*/
    if(_actions.size() == 1){
        setCurrentText(itemText(0));
    }
}

void ComboBox::setCurrentText(const QString& text){
    QString str(text);
    growMaximumWidthFromText(str);
    str.prepend("  ");
    str.append("  ");
    assert(_currentText);
    _currentText->setText(str);
    // if no action matches this text, set the index to a dirty value
    int index = -1;
    for (U32 i = 0; i < _actions.size(); ++i) {
        if(_actions[i]->text() == text){
            index = i;
            break;
        }
    }
    if (_currentIndex != index) {
        _currentIndex = index;
#pragma message WARN("[FD->AG] why not emit currentIndexChanged?")
        // FIXME: why not emit currentIndexChanged?
        //emit currentIndexChanged(_currentIndex);
    }
    //emit currentIndexChanged(text);
}

void ComboBox::setMaximumWidthFromText(const QString& str)
{
    int w = _currentText->fontMetrics().width(str+"    ");
    setMaximumWidth(w);
}

void ComboBox::growMaximumWidthFromText(const QString& str)
{
    int w = _currentText->fontMetrics().width(str+"    ");
    if (w > maximumWidth()) {
        setMaximumWidth(w);
    }
}

QString ComboBox::text() const{
    return _currentText->text();
}

int ComboBox::activeIndex() const{
    return _currentIndex;
}

QString ComboBox::getCurrentIndexText() const {
    assert(_currentIndex < (int)_actions.size());
    return _actions[_currentIndex]->text();
}

void ComboBox::setCurrentIndex(int index)
{
    QString str;
    QString text;
    if (0 <= index && index < (int)_actions.size()) {
        text = _actions[index]->text();
    }
    str = text;
    /*before displaying,prepend and append the text by some spacing.
     This is a dirty way to do this but QLayout::addSpacing() doesn't preserve
     the same style for the label.*/
#if IS_MAXIMUMTEXTSIZE_USEFUL
    int dsize = _maximumTextSize - str.size();
    dsize/=2;
    str.prepend("  ");
    for (int i = 0; i < dsize ; ++i) {str.prepend(" ");}
    str.append("  ");
    for (int i = 0; i < dsize ; ++i) {str.append(" ");}
#endif
    str.prepend("  ");
    str.append("  ");
    _currentText->setText(str);
    // already called growMaximumWidthFromText() from addItem() and insertItem()
    //setMaximumWidthFromText(str);
#pragma message WARN("[FD->AG] is it OK to emit currentIndexChanged only if the index changed?")
    if (_currentIndex != index) {
        _currentIndex = index;
        emit currentIndexChanged(_currentIndex);
    }
    //emit currentIndexChanged(text);
}

void ComboBox::addSeparator(){
    _separators.push_back(_actions.size()-1);
}
void ComboBox::insertSeparator(int index){
    assert(index >= 0);
    _separators.push_back(index);
}

QString ComboBox::itemText(int index) const{
    if(0 <= index && index < (int)_actions.size()) {
        assert(_actions[index]);
        return _actions[index]->text();
    } else {
        return "";
    }
}
int ComboBox::itemIndex(const QString& str) const{
    for (U32 i = 0; i < _actions.size(); ++i) {
        if (_actions[i]->text() == str) {
            return i;
        }
    }
    return -1;
}

void ComboBox::removeItem(const QString& item){
    for (U32 i = 0; i < _actions.size(); ++i) {
        assert(_actions[i]);
        if (_actions[i]->text() == item) {
            _actions.erase(_actions.begin()+i);
            assert(_currentText);
            if (_currentText->text() == item) {
                setCurrentIndex(i-1);
            }
            /*adjust separators that were placed after this item*/
            for (U32 j = 0; j < _separators.size(); ++j) {
                if (_separators[j] >= (int)i) {
                    --_separators[j];
                }
            }
        }
    }
#if IS_MAXIMUMTEXTSIZE_USEFUL
    /*we also need to re-calculate the maximum text size*/
    _maximumTextSize = 0;
    for (U32 i = 0; i < _actions.size(); ++i) {
        assert(_actions[i]);
        if (_actions[i]->text().size() > _maximumTextSize) {
            _maximumTextSize = _actions[i]->text().size();
        }
    }
#endif
}

void ComboBox::clear(){
    _actions.clear();
    _menu->clear();
    _separators.clear();
    _currentIndex = 0;
#if IS_MAXIMUMTEXTSIZE_USEFUL
    _maximumTextSize = 0;
#endif
}

void ComboBox::setItemText(int index,const QString& item){
    assert(0 <= index && index < (int)_actions.size());
    assert(_actions[index]);
    _actions[index]->setText(item);
    growMaximumWidthFromText(item);
#if IS_MAXIMUMTEXTSIZE_USEFUL
    /*we also need to re-calculate the maximum text size*/
    _maximumTextSize = 0;
    for (U32 i = 0; i < _actions.size(); ++i) {
        assert(_actions[i]);
        if (_actions[i]->text().size() > _maximumTextSize) {
            _maximumTextSize = _actions[i]->text().size();
        }
    }
#endif
}

void ComboBox::setItemShortcut(int index,const QKeySequence& sequence){
    assert(0 <= index && index < (int)_actions.size());
    assert(_actions[index]);
    _actions[index]->setShortcut(sequence);
}

void ComboBox::setItemIcon(int index,const QIcon& icon){
    assert(0 <= index && index < (int)_actions.size());
    assert(_actions[index]);
    _actions[index]->setIcon(icon);
}
void ComboBox::disableItem(int index){
    assert(0 <= index && index < (int)_actions.size());
    assert(_actions[index]);
    _actions[index]->setEnabled(false);
}

void ComboBox::enableItem(int index){
    assert(0 <= index && index < (int)_actions.size());
    assert(_actions[index]);
    _actions[index]->setEnabled(true);
}

void ComboBox::setAnimation(int i){
    animation = i;
    style()->unpolish(this);
    style()->polish(this);
    repaint();
}
<|endoftext|>


<commit_before>// @(#):$Id$
// Author: Andrei Gheata   01/03/11

/*************************************************************************
 * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

////////////////////////////////////////////////////////////////////////////
//
// TGeoBranchArray - An array of daughter indices making a geometry path.
//   Can be used to backup/restore a state. To setup an object of this type,
// one should use:
//   TGeoBranchArray *array = new TGeoBranchArray(level);
//   array->InitFromNavigator(nav); (To initialize from current navigator state)
// The navigator can be updated to reflect this path array:
//   array->UpdateNavigator();
//
/////////////////////////////////////////////////////////////////////////////

#include "TGeoBranchArray.h"

#include "TMath.h"
#include "TThread.h"
#include "TString.h"
#include "TGeoNavigator.h"
#include "TGeoCache.h"
#include "TGeoManager.h"

ClassImp(TGeoBranchArray)

//______________________________________________________________________________
TGeoBranchArray::TGeoBranchArray(Int_t maxlevel)
                :fLevel(-1),
                 fMaxLevel(maxlevel),
                 fMatrix(),
                 fArray(&fRealArray[0])
{
// Constructor. Alocates the array with a size given by level.
   memset(fRealArray, 0, fMaxLevel*sizeof(TGeoNode*));
}

//______________________________________________________________________________
TGeoBranchArray * TGeoBranchArray::MakeInstance(size_t maxlevel)
{
// Make an instance of the class which allocates the node array. To be
// released using ReleaseInstance. If addr is non-zero, the user promised that
// addr contains at least that many bytes:  size_t needed = SizeOf(maxlevel);
   TGeoBranchArray* ba = 0;
   size_t needed = SizeOf(maxlevel);
   char *ptr = new char[ needed ];
   if (!ptr) return 0;
   new (ptr) TGeoBranchArray(maxlevel);
   ba = reinterpret_cast<TGeoBranchArray*>(ptr);
   ba->SetBit(kBASelfAlloc, kTRUE);
   return ba;
}

//______________________________________________________________________________
TGeoBranchArray * TGeoBranchArray::MakeInstanceAt(size_t maxlevel, void *addr)
{
   // Make an instance of the class which allocates the node array. To be
   // released using ReleaseInstance. If addr is non-zero, the user promised that
   // addr contains at least that many bytes:  size_t needed = SizeOf(maxlevel);
   TGeoBranchArray* ba = 0;
   new (addr) TGeoBranchArray(maxlevel);
   ba = reinterpret_cast<TGeoBranchArray*>(addr);
   ba->SetBit(kBASelfAlloc, kFALSE);
   return ba;
}

//______________________________________________________________________________
TGeoBranchArray * TGeoBranchArray::MakeCopy(const TGeoBranchArray &other)
{
// Make a copy of a branch array at the location (if indicated)
   TGeoBranchArray *copy = 0;
   size_t needed = SizeOf(other.fMaxLevel);
   char *ptr = new char[ needed ];
   if (!ptr) return 0;
   new (ptr) TGeoBranchArray(other.fMaxLevel);
   copy = reinterpret_cast<TGeoBranchArray*>(ptr);
   copy->SetBit(kBASelfAlloc, kTRUE);
   copy->fLevel = other.fLevel;
   copy->fMatrix = other.fMatrix;
   if (other.fLevel+1) memcpy(copy->fArray, other.fArray, (other.fLevel+1)*sizeof(TGeoNode*));
   return copy;
}

//______________________________________________________________________________
TGeoBranchArray * TGeoBranchArray::MakeCopyAt(const TGeoBranchArray &other, void *addr)
{
   // Make a copy of a branch array at the location (if indicated)
   TGeoBranchArray *copy = 0;
   new (addr) TGeoBranchArray(other.fMaxLevel);
   copy = reinterpret_cast<TGeoBranchArray*>(addr);
   copy->SetBit(kBASelfAlloc, kFALSE);
   copy->fLevel = other.fLevel;
   copy->fMatrix = other.fMatrix;
   if (other.fLevel+1) memcpy(copy->fArray, other.fArray, (other.fLevel+1)*sizeof(TGeoNode*));
   return copy;
}

//______________________________________________________________________________
void TGeoBranchArray::CopyTo(TGeoBranchArray *dest)
{
// Raw memcpy of the branch array content to an existing destination.
   memcpy(dest->DataStart(), DataStart(), DataSize());
   dest->fArray = &(dest->fRealArray[0]);
}

//______________________________________________________________________________
void TGeoBranchArray::ReleaseInstance(TGeoBranchArray *obj)
{
// Releases the space allocated for the object
   obj->~TGeoBranchArray();
   if (obj->TestBit(kBASelfAlloc)) delete [] (char*)obj;
}

//______________________________________________________________________________
void TGeoBranchArray::UpdateArray(size_t nobj)
{
// Updates the internal addresses for n contiguous objects which have the same
// fMaxLevel
// Updates the internal addresses for n contiguous objects which have the same fMaxLevel
   size_t needed = SizeOf();
//   char *where = &fArray;
//   for (size_t i=0; i<nobj; ++i, where += needed) {
//      TGeoNode ***array =  reinterpret_cast<TGeoNode***>(where);
//      *array = ((void**)where)+1;
//   }
   char *where = reinterpret_cast<char*>(this);
   for (size_t i=0; i<nobj; ++i, where += needed) {
      TGeoBranchArray *obj = reinterpret_cast<TGeoBranchArray*>(where);
      obj->fArray = &(obj->fRealArray[0]);
   }
}

//______________________________________________________________________________
TGeoBranchArray::TGeoBranchArray(const TGeoBranchArray&  other)
                :TObject(other),
                 fLevel(other.fLevel),
                 fMaxLevel(other.fMaxLevel),
                 fMatrix(other.fMatrix),
                 fArray(NULL)
{
// Copy constructor. Not callable anymore. Use TGeoBranchArray::MakeCopy instead
   if (fMaxLevel) {
      fArray = new TGeoNode*[fMaxLevel];
      if (fLevel+1) memcpy(fArray, other.fArray, (fLevel+1)*sizeof(TGeoNode*));
   }
}

//______________________________________________________________________________
TGeoBranchArray& TGeoBranchArray::operator=(const TGeoBranchArray& other)
{
// Assignment. Not valid anymore. Use TGeoBranchArray::MakeCopy instead
   if (&other == this) return *this;
//   TThread::Lock();
//   TObject::operator=(other);
   fLevel = other.fLevel;
   fMatrix.CopyFrom(&other.fMatrix);
   if (fLevel+1) memcpy(fArray, other.fArray, (fLevel+1)*sizeof(TGeoNode*));
//   SetBit(other.TestBit(kBASelfAlloc));
//   TThread::UnLock();
   return *this;
}

//______________________________________________________________________________
void TGeoBranchArray::AddLevel(Int_t dindex)
{
// Add and extra daughter to the current path array. No validity check performed !
   if (fLevel<=0) {
      Error("AddLevel", "You must initialize from navigator or copy from another branch array first.");
      return;
   }
   if (fLevel>fMaxLevel) {
      Fatal("AddLevel", "Max level = %d reached\n", fMaxLevel);
      return;
   }
   fLevel++;
/*
   if (fLevel+1>fMaxLevel) {
      TGeoNode **array = new TGeoNode*[fLevel+1];
      memcpy(array, fArray, fLevel*sizeof(TGeoNode*));
      delete [] fArray;
      fArray = array;
   }
*/
   fArray[fLevel] = fArray[fLevel-1]->GetVolume()->GetNode(dindex);
}

//______________________________________________________________________________
Bool_t TGeoBranchArray::operator ==(const TGeoBranchArray& other) const
{
// Is equal operator.
   Int_t value = Compare(&other);
   if (value==0) return kTRUE;
   return kFALSE;
}

//______________________________________________________________________________
Bool_t TGeoBranchArray::operator !=(const TGeoBranchArray& other) const
{
// Not equal operator.
   Int_t value = Compare(&other);
   if (value!=0) return kTRUE;
   return kFALSE;
}

//______________________________________________________________________________
Bool_t TGeoBranchArray::operator >(const TGeoBranchArray& other) const
{
// Is equal operator.
   Int_t value = Compare(&other);
   if (value>0) return kTRUE;
   return kFALSE;
}

//______________________________________________________________________________
Bool_t TGeoBranchArray::operator <(const TGeoBranchArray& other) const
{
// Is equal operator.
   Int_t value = Compare(&other);
   if (value<0) return kTRUE;
   return kFALSE;
}

//______________________________________________________________________________
Bool_t TGeoBranchArray::operator >=(const TGeoBranchArray& other) const
{
// Is equal operator.
   Int_t value = Compare(&other);
   if (value>=0) return kTRUE;
   return kFALSE;
}

//______________________________________________________________________________
Bool_t TGeoBranchArray::operator <=(const TGeoBranchArray& other) const
{
// Is equal operator.
   Int_t value = Compare(&other);
   if (value<=0) return kTRUE;
   return kFALSE;
}

//______________________________________________________________________________
Long64_t TGeoBranchArray::BinarySearch(Long64_t n, const TGeoBranchArray **array, TGeoBranchArray *value)
{
// Binary search in an array of n pointers to branch arrays, to locate value.
// Returns element index or index of nearest element smaller than value
   Long64_t nabove, nbelow, middle;
   const TGeoBranchArray *pind;
   nabove = n+1;
   nbelow = 0;
   while(nabove-nbelow > 1) {
      middle = (nabove+nbelow)/2;
      pind = array[middle-1];
      if (*value == *pind) return middle-1;
      if (*value  < *pind) nabove = middle;
      else                          nbelow = middle;
   }
   return nbelow-1;
}

//______________________________________________________________________________
Int_t TGeoBranchArray::Compare(const TObject *obj) const
{
// Compare with other object of same type. Returns -1 if this is smaller (first
// smaller array value prevails), 0 if equal (size and values) and 1 if this is
// larger.
   Int_t i;
   TGeoBranchArray *other = (TGeoBranchArray*)obj;
   Int_t otherLevel = other->GetLevel();
   Int_t maxLevel = TMath::Min(fLevel, otherLevel);
   TGeoNode **otherArray = other->GetArray();
   for (i=0; i<maxLevel+1; i++) {
      if (fArray[i]==otherArray[i]) continue;
      if ((Long64_t)fArray[i]<(Long64_t)otherArray[i]) return -1;
      return 1;
   }
   if (fLevel==otherLevel) return 0;
   if (fLevel<otherLevel) return -1;
   return 1;
}

//______________________________________________________________________________
void TGeoBranchArray::CleanMatrix()
{
// Garbage collect the stored matrix.
}

//______________________________________________________________________________
void TGeoBranchArray::Init(TGeoNode **branch, TGeoMatrix *global, Int_t level)
{
// Init the branch array from an array of nodes, the global matrix for the path and
// the level.
   fMatrix.CopyFrom(global);
   if (level>fMaxLevel) {
      Fatal("Init", "Requested level %d exceeds maximum level %d", level+1, fMaxLevel);
      return;
   }
   fLevel = level;
   memcpy(fArray, branch, (fLevel+1)*sizeof(TGeoNode*));
}

//______________________________________________________________________________
void TGeoBranchArray::InitFromNavigator(TGeoNavigator *nav)
{
// Init the branch array from current navigator state.
   TGeoNodeCache *cache = nav->GetCache();
   const TGeoNode **branch = (const TGeoNode**)cache->GetBranch();
   Int_t level = cache->GetLevel();
   fMatrix.CopyFrom(cache->GetCurrentMatrix());
   if (level>fMaxLevel) {
      Fatal("InitFromNavigator", "Requested level %d exceeds maximum level %d", level+1, fMaxLevel);
      return;
   }
   fLevel = level;
   memcpy(fArray, branch, (fLevel+1)*sizeof(TGeoNode*));
   if (nav->IsOutside()) fLevel = -1;
}

//______________________________________________________________________________
void TGeoBranchArray::GetPath(TString &path) const
{
// Fill path pointed by the array.
   path = "";
   if (!fArray || !fArray[0]) return;
   for (Int_t i=0; i<fLevel+1; i++) {
      path += "/";
      path += fArray[i]->GetName();
   }
}

//______________________________________________________________________________
void TGeoBranchArray::Print(Option_t *) const
{
// Print branch information
   TString path;
   GetPath(path);
   printf("branch:    %s\n", path.Data());
}

//______________________________________________________________________________
void TGeoBranchArray::Sort(Int_t n, TGeoBranchArray **array, Int_t *index, Bool_t down)
{
// Sorting of an array of branch array pointers.
   for (Int_t i=0; i<n; i++) index[i] = i;
   if (down)
      std::sort(index, index + n, compareBAdesc(array));
   else
      std::sort(index, index + n, compareBAasc(array));
}

//______________________________________________________________________________
void TGeoBranchArray::UpdateNavigator(TGeoNavigator *nav) const
{
// Update the navigator to reflect the branch.
   nav->CdTop();
   if (fLevel<0) {nav->SetOutside(kTRUE); return;}
   for (Int_t i=1; i<fLevel+1; i++) nav->CdDown(fArray[i]);
}
<commit_msg>Fix to allow AddLevel to work<commit_after>// @(#):$Id$
// Author: Andrei Gheata   01/03/11

/*************************************************************************
 * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

////////////////////////////////////////////////////////////////////////////
//
// TGeoBranchArray - An array of daughter indices making a geometry path.
//   Can be used to backup/restore a state. To setup an object of this type,
// one should use:
//   TGeoBranchArray *array = new TGeoBranchArray(level);
//   array->InitFromNavigator(nav); (To initialize from current navigator state)
// The navigator can be updated to reflect this path array:
//   array->UpdateNavigator();
//
/////////////////////////////////////////////////////////////////////////////

#include "TGeoBranchArray.h"

#include "TMath.h"
#include "TThread.h"
#include "TString.h"
#include "TGeoNavigator.h"
#include "TGeoCache.h"
#include "TGeoManager.h"

ClassImp(TGeoBranchArray)

//______________________________________________________________________________
TGeoBranchArray::TGeoBranchArray(Int_t maxlevel)
                :fLevel(-1),
                 fMaxLevel(maxlevel),
                 fMatrix(),
                 fArray(&fRealArray[0])
{
// Constructor. Alocates the array with a size given by level.
   memset(fRealArray, 0, fMaxLevel*sizeof(TGeoNode*));
}

//______________________________________________________________________________
TGeoBranchArray * TGeoBranchArray::MakeInstance(size_t maxlevel)
{
// Make an instance of the class which allocates the node array. To be
// released using ReleaseInstance. If addr is non-zero, the user promised that
// addr contains at least that many bytes:  size_t needed = SizeOf(maxlevel);
   TGeoBranchArray* ba = 0;
   size_t needed = SizeOf(maxlevel);
   char *ptr = new char[ needed ];
   if (!ptr) return 0;
   new (ptr) TGeoBranchArray(maxlevel);
   ba = reinterpret_cast<TGeoBranchArray*>(ptr);
   ba->SetBit(kBASelfAlloc, kTRUE);
   return ba;
}

//______________________________________________________________________________
TGeoBranchArray * TGeoBranchArray::MakeInstanceAt(size_t maxlevel, void *addr)
{
   // Make an instance of the class which allocates the node array. To be
   // released using ReleaseInstance. If addr is non-zero, the user promised that
   // addr contains at least that many bytes:  size_t needed = SizeOf(maxlevel);
   TGeoBranchArray* ba = 0;
   new (addr) TGeoBranchArray(maxlevel);
   ba = reinterpret_cast<TGeoBranchArray*>(addr);
   ba->SetBit(kBASelfAlloc, kFALSE);
   return ba;
}

//______________________________________________________________________________
TGeoBranchArray * TGeoBranchArray::MakeCopy(const TGeoBranchArray &other)
{
// Make a copy of a branch array at the location (if indicated)
   TGeoBranchArray *copy = 0;
   size_t needed = SizeOf(other.fMaxLevel);
   char *ptr = new char[ needed ];
   if (!ptr) return 0;
   new (ptr) TGeoBranchArray(other.fMaxLevel);
   copy = reinterpret_cast<TGeoBranchArray*>(ptr);
   copy->SetBit(kBASelfAlloc, kTRUE);
   copy->fLevel = other.fLevel;
   copy->fMatrix = other.fMatrix;
   if (other.fLevel+1) memcpy(copy->fArray, other.fArray, (other.fLevel+1)*sizeof(TGeoNode*));
   return copy;
}

//______________________________________________________________________________
TGeoBranchArray * TGeoBranchArray::MakeCopyAt(const TGeoBranchArray &other, void *addr)
{
   // Make a copy of a branch array at the location (if indicated)
   TGeoBranchArray *copy = 0;
   new (addr) TGeoBranchArray(other.fMaxLevel);
   copy = reinterpret_cast<TGeoBranchArray*>(addr);
   copy->SetBit(kBASelfAlloc, kFALSE);
   copy->fLevel = other.fLevel;
   copy->fMatrix = other.fMatrix;
   if (other.fLevel+1) memcpy(copy->fArray, other.fArray, (other.fLevel+1)*sizeof(TGeoNode*));
   return copy;
}

//______________________________________________________________________________
void TGeoBranchArray::CopyTo(TGeoBranchArray *dest)
{
// Raw memcpy of the branch array content to an existing destination.
   memcpy(dest->DataStart(), DataStart(), DataSize());
   dest->fArray = &(dest->fRealArray[0]);
}

//______________________________________________________________________________
void TGeoBranchArray::ReleaseInstance(TGeoBranchArray *obj)
{
// Releases the space allocated for the object
   obj->~TGeoBranchArray();
   if (obj->TestBit(kBASelfAlloc)) delete [] (char*)obj;
}

//______________________________________________________________________________
void TGeoBranchArray::UpdateArray(size_t nobj)
{
// Updates the internal addresses for n contiguous objects which have the same
// fMaxLevel
// Updates the internal addresses for n contiguous objects which have the same fMaxLevel
   size_t needed = SizeOf();
//   char *where = &fArray;
//   for (size_t i=0; i<nobj; ++i, where += needed) {
//      TGeoNode ***array =  reinterpret_cast<TGeoNode***>(where);
//      *array = ((void**)where)+1;
//   }
   char *where = reinterpret_cast<char*>(this);
   for (size_t i=0; i<nobj; ++i, where += needed) {
      TGeoBranchArray *obj = reinterpret_cast<TGeoBranchArray*>(where);
      obj->fArray = &(obj->fRealArray[0]);
   }
}

//______________________________________________________________________________
TGeoBranchArray::TGeoBranchArray(const TGeoBranchArray&  other)
                :TObject(other),
                 fLevel(other.fLevel),
                 fMaxLevel(other.fMaxLevel),
                 fMatrix(other.fMatrix),
                 fArray(NULL)
{
// Copy constructor. Not callable anymore. Use TGeoBranchArray::MakeCopy instead
   if (fMaxLevel) {
      fArray = new TGeoNode*[fMaxLevel];
      if (fLevel+1) memcpy(fArray, other.fArray, (fLevel+1)*sizeof(TGeoNode*));
   }
}

//______________________________________________________________________________
TGeoBranchArray& TGeoBranchArray::operator=(const TGeoBranchArray& other)
{
// Assignment. Not valid anymore. Use TGeoBranchArray::MakeCopy instead
   if (&other == this) return *this;
//   TThread::Lock();
//   TObject::operator=(other);
   fLevel = other.fLevel;
   fMatrix.CopyFrom(&other.fMatrix);
   if (fLevel+1) memcpy(fArray, other.fArray, (fLevel+1)*sizeof(TGeoNode*));
//   SetBit(other.TestBit(kBASelfAlloc));
//   TThread::UnLock();
   return *this;
}

//______________________________________________________________________________
void TGeoBranchArray::AddLevel(Int_t dindex)
{
// Add and extra daughter to the current path array. No validity check performed !
   if (fLevel<0) {
      Error("AddLevel", "You must initialize from navigator or copy from another branch array first.");
      return;
   }
   if (fLevel>fMaxLevel) {
      Fatal("AddLevel", "Max level = %d reached\n", fMaxLevel);
      return;
   }
   fLevel++;
/*
   if (fLevel+1>fMaxLevel) {
      TGeoNode **array = new TGeoNode*[fLevel+1];
      memcpy(array, fArray, fLevel*sizeof(TGeoNode*));
      delete [] fArray;
      fArray = array;
   }
*/
   fArray[fLevel] = fArray[fLevel-1]->GetVolume()->GetNode(dindex);
}

//______________________________________________________________________________
Bool_t TGeoBranchArray::operator ==(const TGeoBranchArray& other) const
{
// Is equal operator.
   Int_t value = Compare(&other);
   if (value==0) return kTRUE;
   return kFALSE;
}

//______________________________________________________________________________
Bool_t TGeoBranchArray::operator !=(const TGeoBranchArray& other) const
{
// Not equal operator.
   Int_t value = Compare(&other);
   if (value!=0) return kTRUE;
   return kFALSE;
}

//______________________________________________________________________________
Bool_t TGeoBranchArray::operator >(const TGeoBranchArray& other) const
{
// Is equal operator.
   Int_t value = Compare(&other);
   if (value>0) return kTRUE;
   return kFALSE;
}

//______________________________________________________________________________
Bool_t TGeoBranchArray::operator <(const TGeoBranchArray& other) const
{
// Is equal operator.
   Int_t value = Compare(&other);
   if (value<0) return kTRUE;
   return kFALSE;
}

//______________________________________________________________________________
Bool_t TGeoBranchArray::operator >=(const TGeoBranchArray& other) const
{
// Is equal operator.
   Int_t value = Compare(&other);
   if (value>=0) return kTRUE;
   return kFALSE;
}

//______________________________________________________________________________
Bool_t TGeoBranchArray::operator <=(const TGeoBranchArray& other) const
{
// Is equal operator.
   Int_t value = Compare(&other);
   if (value<=0) return kTRUE;
   return kFALSE;
}

//______________________________________________________________________________
Long64_t TGeoBranchArray::BinarySearch(Long64_t n, const TGeoBranchArray **array, TGeoBranchArray *value)
{
// Binary search in an array of n pointers to branch arrays, to locate value.
// Returns element index or index of nearest element smaller than value
   Long64_t nabove, nbelow, middle;
   const TGeoBranchArray *pind;
   nabove = n+1;
   nbelow = 0;
   while(nabove-nbelow > 1) {
      middle = (nabove+nbelow)/2;
      pind = array[middle-1];
      if (*value == *pind) return middle-1;
      if (*value  < *pind) nabove = middle;
      else                          nbelow = middle;
   }
   return nbelow-1;
}

//______________________________________________________________________________
Int_t TGeoBranchArray::Compare(const TObject *obj) const
{
// Compare with other object of same type. Returns -1 if this is smaller (first
// smaller array value prevails), 0 if equal (size and values) and 1 if this is
// larger.
   Int_t i;
   TGeoBranchArray *other = (TGeoBranchArray*)obj;
   Int_t otherLevel = other->GetLevel();
   Int_t maxLevel = TMath::Min(fLevel, otherLevel);
   TGeoNode **otherArray = other->GetArray();
   for (i=0; i<maxLevel+1; i++) {
      if (fArray[i]==otherArray[i]) continue;
      if ((Long64_t)fArray[i]<(Long64_t)otherArray[i]) return -1;
      return 1;
   }
   if (fLevel==otherLevel) return 0;
   if (fLevel<otherLevel) return -1;
   return 1;
}

//______________________________________________________________________________
void TGeoBranchArray::CleanMatrix()
{
// Garbage collect the stored matrix.
}

//______________________________________________________________________________
void TGeoBranchArray::Init(TGeoNode **branch, TGeoMatrix *global, Int_t level)
{
// Init the branch array from an array of nodes, the global matrix for the path and
// the level.
   fMatrix.CopyFrom(global);
   if (level>fMaxLevel) {
      Fatal("Init", "Requested level %d exceeds maximum level %d", level+1, fMaxLevel);
      return;
   }
   fLevel = level;
   memcpy(fArray, branch, (fLevel+1)*sizeof(TGeoNode*));
}

//______________________________________________________________________________
void TGeoBranchArray::InitFromNavigator(TGeoNavigator *nav)
{
// Init the branch array from current navigator state.
   TGeoNodeCache *cache = nav->GetCache();
   const TGeoNode **branch = (const TGeoNode**)cache->GetBranch();
   Int_t level = cache->GetLevel();
   fMatrix.CopyFrom(cache->GetCurrentMatrix());
   if (level>fMaxLevel) {
      Fatal("InitFromNavigator", "Requested level %d exceeds maximum level %d", level+1, fMaxLevel);
      return;
   }
   fLevel = level;
   memcpy(fArray, branch, (fLevel+1)*sizeof(TGeoNode*));
   if (nav->IsOutside()) fLevel = -1;
}

//______________________________________________________________________________
void TGeoBranchArray::GetPath(TString &path) const
{
// Fill path pointed by the array.
   path = "";
   if (!fArray || !fArray[0]) return;
   for (Int_t i=0; i<fLevel+1; i++) {
      path += "/";
      path += fArray[i]->GetName();
   }
}

//______________________________________________________________________________
void TGeoBranchArray::Print(Option_t *) const
{
// Print branch information
   TString path;
   GetPath(path);
   printf("branch:    %s\n", path.Data());
}

//______________________________________________________________________________
void TGeoBranchArray::Sort(Int_t n, TGeoBranchArray **array, Int_t *index, Bool_t down)
{
// Sorting of an array of branch array pointers.
   for (Int_t i=0; i<n; i++) index[i] = i;
   if (down)
      std::sort(index, index + n, compareBAdesc(array));
   else
      std::sort(index, index + n, compareBAasc(array));
}

//______________________________________________________________________________
void TGeoBranchArray::UpdateNavigator(TGeoNavigator *nav) const
{
// Update the navigator to reflect the branch.
   nav->CdTop();
   if (fLevel<0) {nav->SetOutside(kTRUE); return;}
   for (Int_t i=1; i<fLevel+1; i++) nav->CdDown(fArray[i]);
}
<|endoftext|>


<commit_before>/** \copyright
 * Copyright (c) 2013, Stuart W Baker
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * \file StateFlow.hxx
 *
 * Defines a type of state machine flow used within class Service.
 *
 * @author Stuart W Baker
 * @date 25 December 2013
 */

#ifndef _StateFlow_hxx_
#define _StateFlow_hxx_

#include <type_traits>

#include "executor/Allocator.hxx"
#include "executor/Message.hxx"
#include "utils/BufferQueue.hxx"

#define STATE(_fn) (Callback)(&std::remove_reference<decltype(*this)>::type::_fn)

/** Begin the definition of a StateFlow.
 * @param _name the class name of the StateFlow derived object
 * @param _priorities number of input queue priorities
 */
#define STATE_FLOW_START(_name, _priorities)    \
    class _name : public StateFlow<_priorities> \
    {                                           \
    public:                                     \
        _name(Service *service)                 \
            : StateFlow<_priorities>(service)   \
        {                                       \
        }                                       \
                                                \
        ~_name()                                \
        {                                       \
        }                                       \
                                                \
    private:                                    \
        Action entry(Message *msg);             \
                                                \
        _name();                                \
                                                \
        DISALLOW_COPY_AND_ASSIGN(_name);

/** Begin the definition of a StateFlow that includes timeouts.
 * @param _name the class name of the StateFlow derived object
 * @param _priorities number of input queue priorities
 */
#define STATE_FLOW_START_WITH_TIMER(_name, _priorities)                     \
    class _name : public StateFlow<_priorities>                             \
    {                                                                       \
    public:                                                                 \
        _name(Service *service)                                             \
            : StateFlow<_priorities>(service),                              \
              timer(TIMEOUT_FROM(service, state_flow_timeout),              \
                    service,                                                \
                    this),                                                  \
              timerMsg(NULL)                                                \
        {                                                                   \
        }                                                                   \
                                                                            \
        ~_name()                                                            \
        {                                                                   \
        }                                                                   \
                                                                            \
        void timeout()                                                      \
        {                                                                   \
            timerMsg ? me()->send(timerMsg) : ;                             \
            timerMsg = NULL;                                                \
        }                                                                   \
                                                                            \
        void trigger()                                                      \
        {                                                                   \
            timer.trigger();                                                \
        }                                                                   \
                                                                            \
    private:                                                                \
        Action entry(Message *msg);                                         \
                                                                            \
        Action timeout_and_call(Callback c, Message *msg, long long period) \
        {                                                                   \
            msg->id(msg->id() | Message::IN_PROCESS_MSK);                   \
            timerMsg = msg;                                                 \
            timer.start(period);                                            \
            return Action(c);                                               \
        }                                                                   \
                                                                            \
        Timer timer;                                                        \
        Message *timerMsg;                                                  \
                                                                            \
        bool early()                                                        \
        {                                                                   \
            return timer.early();                                           \
        }                                                                   \
                                                                            \
        _name();                                                            \
                                                                            \
        DISALLOW_COPY_AND_ASSIGN(_name);

/** Declare a state callback in a StateFlow.
 * @param _state the method name of the StateFlow state callback
 */
#define STATE_FLOW_STATE(_state) Action _state(Message *msg);

/** End the definition of a StateFlow.
 */
#define STATE_FLOW_END() };

/** Runs incoming Messages through a State Flow.
 */
class StateFlowBase
{
protected:
    /** Constructor.
     * @param service Service that this state flow is part of
     * @param size number of queues in the list
     */
    StateFlowBase(Service *service)
        : service(service),
          state(STATE(terminated))
    {
    }

    /** Destructor.
     */
    ~StateFlowBase()
    {
    }

    /* forward prototype */
    class Action;

    /** State Flow callback prototype
     */
    typedef Action (StateFlowBase::*Callback)(Message *);

    /** Return type for a state flow callback.
     */
    class Action
    {
    public:
        /** Constructor.
         */
        Action(Callback s)
            : nextState(s)
        {
        }

        /** Get the next state for the StateFlowAction.
         */
        Callback next_state()
        {
            return nextState;
        }

    private:
        /** next state in state flow */
        Callback nextState;
    };

    /** Entry into the StateFlow activity.  Pure virtual which must be
     * defined by derived class.
     * @param msg Message belonging to the state flow
     * @return function pointer to next state
     */
    virtual Action entry(Message *msg) = 0;

    /** Call the current state again.
     * @return function pointer to current state handler
     */
    Action again()
    {
        return Action(state);
    }

    /** Terminate current StateFlow activity.  The message instance is not
     * released before termination.  This is usefull if the message will be
     * reused for the purpose of sending to another StateFlow.
     * @return function pointer to terminated method
     */
    Action exit()
    {
        return STATE(terminated);
    }

    /** Terminate current StateFlow activity. after releasing the message.
     * @param msg to release
     * @return function pointer to terminated method
     */
    Action release_and_exit(Message *msg)
    {
        msg->free();
        return exit();
    }

    /** Imediately call the next state upon return.
     * @param c Callback "state" to move to
     * @return function pointer to passed in callback
     */
    Action call_immediately(Callback c)
    {
        return Action(c);
    }

    /** Wait for resource to become available before proceeding to next state.
     * @param c Callback "state" to move to
     * @param msg Message instance we are waiting on
     * @return function pointer to passed in callback
     */
    Action wait_and_call(Callback c, Message *msg)
    {
        msg->id(msg->id() | Message::IN_PROCESS_MSK);
        return Action(c);
    }

    /** Wait for resource to become available before proceeding to next state.
     * if an immediate allocation can be made, an immediate call to the next
     * state will be made.
     * @param c Callback "state" to move to
     * @param msg Message instance we are waiting on
     * @return function pointer to passed in callback
     */
    template <class T> Action allocate_and_call(Allocator<T> *a, Callback c, Message *msg)
    {
        return a->allocate_immediate(msg) ? call_immediately(c) : Action(c);
    }

    /** Imediately queue up the next callback for this flow through the executor.
     * Similar to @ref call_immediately, except we place this flow on the back
     * of the Executor queue.
     * @param c Callback "state" to move to
     * @param msg Message instance we are acting upon
     * @return function pointer to passed in callback
     */
    Action yeild_and_call(Callback c, Message *msg);

    /** Return a pointer to the service I am bound to.
     * @return pointer to service
     */
    Service *me()
    {
        return service;
    }

    /** Timeout expired.  The expectation is that a derived class will implement
     * this if it desires timeouts.
     */
    virtual void timeout()
    {
    }

private:
    /** Insert a message into one of the work queues.
     * @param msg Message to insert
     * @param index queue index to insert into
     */
    virtual void insert(Message *msg, unsigned index) = 0;

    /** Pull a message out of one of the work queues.
     * @return message out of one of the work queues, NULL if none available
     */
    virtual Message *next() = 0;

    /** Service this StateFlow belongs to */
    Service *service;

    /** Terminate current StateFlow activity.  This method only exists for the
     * purpose of providing a unique address pointer.
     * @param msg unused
     * @return should never return
     */
    Action terminated(Message *msg);

    /** Process an incoming message.
     * @param msg message to process
     * @param priority priority of message
     */
    void process(Message *msg, unsigned priority);

    /** current active state in the flow */
    Callback state;

    /** Allow class Service to access our private members */
    friend class Service;

    /** Allow class Message to access our private and protected members */
    friend class Message;

    /** Default constructor.
     */
    StateFlowBase();

    DISALLOW_COPY_AND_ASSIGN(StateFlowBase);
};

template <unsigned items> class StateFlow : public StateFlowBase,
                                            public QList<Message, items>
{
public:
    /** Constructor.
     * @param service Service that this state flow is part of
     * @param size number of queues in the list
     */
    StateFlow(Service *service)
        : StateFlowBase(service),
          QList<Message, items>()
    {
    }

    /** Destructor.
     */
    ~StateFlow()
    {
    }

protected:
    /** Entry into the StateFlow activity.  Pure virtual which must be
     * defined by derived class.
     * @param msg Message belonging to the state flow
     * @return function pointer to next state
     */
    virtual Action entry(Message *msg) = 0;

private:
    /** Insert a message into one of the work queues.
     * @param msg Message to insert
     * @param index queue index to insert into
     */
    void insert(Message *msg, unsigned index)
    {
        QList<Message, items>::insert(msg, index >= items ? items - 1 : index);
    }

    /** Pull a message out of one of the work queues.
     * @return message out of one of the work queues, NULL if none available
     */
    Message *next()
    {
        return QList<Message, items>::next().item;
    }

    /** Default constructor.
     */
    StateFlow();

    DISALLOW_COPY_AND_ASSIGN(StateFlow);
};

#endif /* _StateFlow_hxx_ */
<commit_msg>Adds current message to stateflowbase and renames 'items' to NUM_PRIO to state flow.<commit_after>/** \copyright
 * Copyright (c) 2013, Stuart W Baker
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * \file StateFlow.hxx
 *
 * Defines a type of state machine flow used within class Service.
 *
 * @author Stuart W Baker
 * @date 25 December 2013
 */

#ifndef _StateFlow_hxx_
#define _StateFlow_hxx_

#include <type_traits>

#include "executor/Allocator.hxx"
#include "executor/Message.hxx"
#include "utils/BufferQueue.hxx"

#define STATE(_fn) (Callback)(&std::remove_reference<decltype(*this)>::type::_fn)

/** Begin the definition of a StateFlow.
 * @param _name the class name of the StateFlow derived object
 * @param _priorities number of input queue priorities
 */
#define STATE_FLOW_START(_name, _priorities)    \
    class _name : public StateFlow<_priorities> \
    {                                           \
    public:                                     \
        _name(Service *service)                 \
            : StateFlow<_priorities>(service)   \
        {                                       \
        }                                       \
                                                \
        ~_name()                                \
        {                                       \
        }                                       \
                                                \
    private:                                    \
        Action entry(Message *msg);             \
                                                \
        _name();                                \
                                                \
        DISALLOW_COPY_AND_ASSIGN(_name);

/** Begin the definition of a StateFlow that includes timeouts.
 * @param _name the class name of the StateFlow derived object
 * @param _priorities number of input queue priorities
 */
#define STATE_FLOW_START_WITH_TIMER(_name, _priorities)                     \
    class _name : public StateFlow<_priorities>                             \
    {                                                                       \
    public:                                                                 \
        _name(Service *service)                                             \
            : StateFlow<_priorities>(service),                              \
              timer(TIMEOUT_FROM(service, state_flow_timeout),              \
                    service,                                                \
                    this),                                                  \
              timerMsg(NULL)                                                \
        {                                                                   \
        }                                                                   \
                                                                            \
        ~_name()                                                            \
        {                                                                   \
        }                                                                   \
                                                                            \
        void timeout()                                                      \
        {                                                                   \
            timerMsg ? me()->send(timerMsg) : ;                             \
            timerMsg = NULL;                                                \
        }                                                                   \
                                                                            \
        void trigger()                                                      \
        {                                                                   \
            timer.trigger();                                                \
        }                                                                   \
                                                                            \
    private:                                                                \
        Action entry(Message *msg);                                         \
                                                                            \
        Action timeout_and_call(Callback c, Message *msg, long long period) \
        {                                                                   \
            msg->id(msg->id() | Message::IN_PROCESS_MSK);                   \
            timerMsg = msg;                                                 \
            timer.start(period);                                            \
            return Action(c);                                               \
        }                                                                   \
                                                                            \
        Timer timer;                                                        \
        Message *timerMsg;                                                  \
                                                                            \
        bool early()                                                        \
        {                                                                   \
            return timer.early();                                           \
        }                                                                   \
                                                                            \
        _name();                                                            \
                                                                            \
        DISALLOW_COPY_AND_ASSIGN(_name);

/** Declare a state callback in a StateFlow.
 * @param _state the method name of the StateFlow state callback
 */
#define STATE_FLOW_STATE(_state) Action _state(Message *msg);

/** End the definition of a StateFlow.
 */
#define STATE_FLOW_END() };

/** Runs incoming Messages through a State Flow.
 */
class StateFlowBase
{
protected:
    /** Constructor.
     * @param service Service that this state flow is part of
     * @param size number of queues in the list
     */
    StateFlowBase(Service *service)
        : service(service),
          state(STATE(terminated))
    {
    }

    /** Destructor.
     */
    ~StateFlowBase()
    {
    }

    /* forward prototype */
    class Action;

    /** State Flow callback prototype
     */
    typedef Action (StateFlowBase::*Callback)(Message *);

    /** Return type for a state flow callback.
     */
    class Action
    {
    public:
        /** Constructor.
         */
        Action(Callback s)
            : nextState(s)
        {
        }

        /** Get the next state for the StateFlowAction.
         */
        Callback next_state()
        {
            return nextState;
        }

    private:
        /** next state in state flow */
        Callback nextState;
    };

    /** Entry into the StateFlow activity.  Pure virtual which must be
     * defined by derived class.
     * @param msg Message belonging to the state flow
     * @return function pointer to next state
     */
    virtual Action entry(Message *msg) = 0;

    /** @returns the current message we are processing. */
    Message* message() { reutrn currentMessage_; }

    /*========== ACTION COMMANDS ===============*/
    /* StateFlow implementations will have to use one of the following commands
     * to return from a state handler to indicate what action to take. */

    /** Call the current state again.
     * @return function pointer to current state handler
     */
    Action again()
    {
        return Action(state);
    }

    /** Terminate current StateFlow activity.  The message instance is not
     * released before termination.  This is usefull if the message will be
     * reused for the purpose of sending to another StateFlow.
     * @return function pointer to terminated method
     */
    Action exit()
    {
        return STATE(terminated);
    }

    /** Terminate current StateFlow activity. after releasing the message.
     * @param msg to release
     * @return function pointer to terminated method
     */
    Action release_and_exit(Message *msg)
    {
        msg->free();
        return exit();
    }

    /** Imediately call the next state upon return.
     * @param c Callback "state" to move to
     * @return function pointer to passed in callback
     */
    Action call_immediately(Callback c)
    {
        return Action(c);
    }

    /** Wait for resource to become available before proceeding to next state.
     * @param c Callback "state" to move to
     * @param msg Message instance we are waiting on
     * @return function pointer to passed in callback
     */
    Action wait_and_call(Callback c, Message *msg)
    {
        msg->id(msg->id() | Message::IN_PROCESS_MSK);
        return Action(c);
    }

    /** Wait for resource to become available before proceeding to next state.
     * if an immediate allocation can be made, an immediate call to the next
     * state will be made.
     * @param c Callback "state" to move to
     * @param msg Message instance we are waiting on
     * @return function pointer to passed in callback
     */
    template <class T> Action allocate_and_call(Allocator<T> *a, Callback c, Message *msg)
    {
        return a->allocate_immediate(msg) ? call_immediately(c) : Action(c);
    }

    /** Imediately queue up the next callback for this flow through the executor.
     * Similar to @ref call_immediately, except we place this flow on the back
     * of the Executor queue.
     * @param c Callback "state" to move to
     * @param msg Message instance we are acting upon
     * @return function pointer to passed in callback
     */
    Action yield_and_call(Callback c, Message *msg);

    /** Return a pointer to the service I am bound to.
     * @return pointer to service
     */
    Service *me()
    {
        return service;
    }

    /** Timeout expired.  The expectation is that a derived class will implement
     * this if it desires timeouts.
     */
    virtual void timeout()
    {
    }

private:
    /** Insert a message into one of the work queues.
     * @param msg Message to insert
     * @param index queue index to insert into
     */
    virtual void insert(Message *msg, unsigned index) = 0;

    /** Pull a message out of one of the work queues.
     * @return message out of one of the work queues, NULL if none available
     */
    virtual Message *next() = 0;

    /** Service this StateFlow belongs to */
    Service *service;

    /** Terminate current StateFlow activity.  This method only exists for the
     * purpose of providing a unique address pointer.
     * @param msg unused
     * @return should never return
     */
    Action terminated(Message *msg);

    /** Process an incoming message.
     * @param msg message to process
     * @param priority priority of message
     */
    void process(Message *msg, unsigned priority);

    /** current active state in the flow */
    Callback state_;

    /** The message we are currently processing */
    Message* currentMessage_;

    /** Default constructor.
     */
    StateFlowBase();

    DISALLOW_COPY_AND_ASSIGN(StateFlowBase);
};

template <unsigned NUM_PRIO> class StateFlow : public StateFlowBase,
                                            public QList<Message, NUM_PRIO>
{
public:
    /** Constructor.
     * @param service Service that this state flow is part of
     * @param size number of queues in the list
     */
    StateFlow(Service *service)
        : StateFlowBase(service),
          QList<Message, NUM_PRIO>()
    {
    }

    /** Destructor.
     */
    ~StateFlow()
    {
    }

protected:
    /** Entry into the StateFlow activity.  Pure virtual which must be
     * defined by derived class.
     * @param msg Message belonging to the state flow
     * @return function pointer to next state
     */
    virtual Action entry(Message *msg) = 0;

private:
    /** Insert a message into one of the work queues.
     * @param msg Message to insert
     * @param index queue index to insert into
     */
    void insert(Message *msg, unsigned index)
    {
        QList<Message, NUM_PRIO>::insert(msg, index >= NUM_PRIO ? NUM_PRIO - 1 : index);
    }

    /** Pull a message out of one of the work queues.
     * @return message out of one of the work queues, NULL if none available
     */
    Message *next()
    {
        return QList<Message, NUM_PRIO>::next().item;
    }

    /** Default constructor.
     */
    StateFlow();

    DISALLOW_COPY_AND_ASSIGN(StateFlow);
};

#endif /* _StateFlow_hxx_ */
<|endoftext|>