<commit_before>// @(#)root/gui:$Name:  $:$Id: TGStatusBar.cxx,v 1.7 2003/11/05 13:08:26 rdm Exp $
// Author: Fons Rademakers   23/01/98

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

    This source is based on Xclass95, a Win95-looking GUI toolkit.
    Copyright (C) 1996, 1997 David Barth, Ricky Ralston, Hector Peraza.

    Xclass95 is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

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

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TGStatusBar                                                          //
//                                                                      //
// Provides a StatusBar widget.                                         //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

#include "TGStatusBar.h"
#include "TGResourcePool.h"
#include "Riostream.h"

const TGFont *TGStatusBar::fgDefaultFont = 0;
TGGC         *TGStatusBar::fgDefaultGC = 0;
TGLayoutHints *TGStatusBar::fgHints = new TGLayoutHints(kLHintsTop | kLHintsLeft, 0, 0, 0, 0);

class TGStatusBarPart : public TGHorizontalFrame {
friend class TGStatusBar;
private:
   TGString  *fStatusInfo;    // status text to be displayed in this part
   Int_t      fYt;            // y position of text in frame
   virtual void DoRedraw();

public:
   TGStatusBarPart(const TGWindow *p, Int_t h, Int_t y, ULong_t back = GetDefaultFrameBackground());
   ~TGStatusBarPart() { delete fStatusInfo; DestroyWindow(); }
   void SetText(TGString *text);
   const TGString *GetText() const { return fStatusInfo; }
};

//______________________________________________________________________________
TGStatusBarPart::TGStatusBarPart(const TGWindow *p, Int_t h, Int_t y, ULong_t back)
   : TGHorizontalFrame(p, 5, 5, kChildFrame | kHorizontalFrame, back)
{
   // Create statusbar part frame. This frame will contain the text for this
   // statusbar part.

   fStatusInfo = 0;
   fYt = y + 1;
   fHeight = h;
   MapWindow();
}

//______________________________________________________________________________
void TGStatusBarPart::SetText(TGString *text)
{
   // Set text in this part of the statusbar.

   if (fStatusInfo) delete fStatusInfo;
   fStatusInfo = text;
   fClient->NeedRedraw(this);
}

//______________________________________________________________________________
void TGStatusBarPart::DoRedraw()
{
   // Draw string in statusbar part frame.

   TGHorizontalFrame::DoRedraw();

   if (fStatusInfo)
      fStatusInfo->Draw(fId, TGStatusBar::GetDefaultGC()(), 3, fYt);
}

ClassImp(TGStatusBar)

//______________________________________________________________________________
TGStatusBar::TGStatusBar(const TGWindow *p, UInt_t w, UInt_t h,
                         UInt_t options, ULong_t back) :
   TGHorizontalFrame(p, w, h, options, back)
{
   // Create a status bar widget. By default it consist of one part.
   // Multiple parts can be created using SetParts().

   fBorderWidth   = 2;
   fStatusPart    = new TGStatusBarPart* [1];
   fParts         = new Int_t [1];
   fXt            = new Int_t [1];
   fParts[0]      = 100;
   fNpart         = 1;
   f3DCorner      = kTRUE;

   int max_ascent, max_descent;
   gVirtualX->GetFontProperties(GetDefaultFontStruct(), max_ascent, max_descent);
   int ht = max_ascent + max_descent;

   fYt = max_ascent;

   fStatusPart[0] = new TGStatusBarPart(this, ht, fYt);
   AddFrame(fStatusPart[0], fgHints);
   Resize(w, ht + 5);
}

//______________________________________________________________________________
TGStatusBar::~TGStatusBar()
{
   // Delete status bar widget.

   for (int i = 0; i < fNpart; i++)
      delete fStatusPart[i];

   delete [] fStatusPart;
   delete [] fParts;
   delete [] fXt;
}

//______________________________________________________________________________
void TGStatusBar::SetText(TGString *text, Int_t partidx)
{
   // Set text in partition partidx in status bar. The TGString is
   // adopted by the status bar.

   if (partidx < 0 || partidx >= fNpart) {
      Error("SetText", "partidx out of range (0,%d)", fNpart-1);
      return;
   }

   fStatusPart[partidx]->SetText(text);
}

//______________________________________________________________________________
void TGStatusBar::SetText(const char *text, Int_t partidx)
{
   // Set text in partion partidx in status bar.

   SetText(new TGString(text), partidx);
}

//______________________________________________________________________________
void TGStatusBar::DrawBorder()
{
   // Draw the status bar border (including cute 3d corner).

   // Current width is known at this stage so calculate fXt's.
   int i;
   for (i = 0; i < fNpart; i++) {
      if (i == 0)
         fXt[i] = 0;
      else
         fXt[i] = fXt[i-1] + (fWidth * fParts[i-1] / 100);
   }

   //TGFrame::DrawBorder();
   for (i = 0; i < fNpart; i++) {
      int xmax, xmin = fXt[i];
      if (i == fNpart-1)
         xmax = fWidth;
      else
         xmax = fXt[i+1] - 2;

      if (i == fNpart-1)
         fStatusPart[i]->MoveResize(fXt[i]+2, 1, xmax - fXt[i] - 15, fHeight - 2);
      else
         fStatusPart[i]->MoveResize(fXt[i]+2, 1, xmax - fXt[i] - 4, fHeight - 2);

      gVirtualX->DrawLine(fId, GetShadowGC()(), xmin, 0, xmax-2, 0);
      gVirtualX->DrawLine(fId, GetShadowGC()(), xmin, 0, xmin, fHeight-2);
      gVirtualX->DrawLine(fId, GetHilightGC()(), xmin, fHeight-1, xmax-1, fHeight-1);
      if (i == fNpart-1)
         gVirtualX->DrawLine(fId, GetHilightGC()(), xmax-1, fHeight-1, xmax-1, 0);
      else
         gVirtualX->DrawLine(fId, GetHilightGC()(), xmax-1, fHeight-1, xmax-1, 1);
   }

   // 3d corner...
   if (f3DCorner) {
      gVirtualX->DrawLine(fId, GetShadowGC()(),  fWidth-3,  fHeight-2, fWidth-2, fHeight-3);
      gVirtualX->DrawLine(fId, GetShadowGC()(),  fWidth-4,  fHeight-2, fWidth-2, fHeight-4);
      gVirtualX->DrawLine(fId, GetHilightGC()(), fWidth-5,  fHeight-2, fWidth-2, fHeight-5);

      gVirtualX->DrawLine(fId, GetShadowGC()(),  fWidth-7,  fHeight-2, fWidth-2, fHeight-7);
      gVirtualX->DrawLine(fId, GetShadowGC()(),  fWidth-8,  fHeight-2, fWidth-2, fHeight-8);
      gVirtualX->DrawLine(fId, GetHilightGC()(), fWidth-9,  fHeight-2, fWidth-2, fHeight-9);

      gVirtualX->DrawLine(fId, GetShadowGC()(),  fWidth-11, fHeight-2, fWidth-2, fHeight-11);
      gVirtualX->DrawLine(fId, GetShadowGC()(),  fWidth-12, fHeight-2, fWidth-2, fHeight-12);
      gVirtualX->DrawLine(fId, GetHilightGC()(), fWidth-13, fHeight-2, fWidth-2, fHeight-13);

      gVirtualX->DrawLine(fId, GetBckgndGC()(),  fWidth-13, fHeight-1, fWidth-1, fHeight-1);
      gVirtualX->DrawLine(fId, GetBckgndGC()(),  fWidth-1,  fHeight-1, fWidth-1, fHeight-13);
   }
}

//______________________________________________________________________________
void TGStatusBar::DoRedraw()
{
   // Redraw status bar.

   // calls DrawBorder()
   TGFrame::DoRedraw();

   for (int i = 0; i < fNpart; i++)
      fStatusPart[i]->DoRedraw();
}

//______________________________________________________________________________
void TGStatusBar::SetParts(Int_t *parts, Int_t npart)
{
   // Divide the status bar in nparts. Size of each part is given in parts
   // array (percentual).

   if (npart < 1) {
      Warning("SetParts", "must be at least one part");
      npart = 1;
   }
   if (npart > 15) {
      Error("SetParts", "to many parts (limit is 15)");
      return;
   }

   int i;
   for (i = 0; i < fNpart; i++)
      delete fStatusPart[i];

   delete [] fStatusPart;
   delete [] fParts;
   delete [] fXt;
   fList->Delete();

   fStatusPart = new TGStatusBarPart* [npart];
   fParts      = new Int_t [npart];
   fXt         = new Int_t [npart];

   int tot = 0;
   for (i = 0; i < npart; i++) {
      fStatusPart[i] = new TGStatusBarPart(this, fHeight, fYt);
      AddFrame(fStatusPart[i], fgHints);
      fParts[i] = parts[i];
      tot += parts[i];
      if (tot > 100)
         Error("SetParts", "sum of part > 100");
   }
   if (tot < 100)
      fParts[npart-1] += 100 - tot;
   fNpart = npart;
}

//______________________________________________________________________________
void TGStatusBar::SetParts(Int_t npart)
{
   // Divide the status bar in npart equal sized parts.

   if (npart < 1) {
      Warning("SetParts", "must be at least one part");
      npart = 1;
   }
   if (npart > 40) {
      Error("SetParts", "to many parts (limit is 40)");
      return;
   }

   int i;
   for (i = 0; i < fNpart; i++)
      delete fStatusPart[i];

   delete [] fStatusPart;
   delete [] fParts;
   delete [] fXt;
   fList->Delete();

   fStatusPart = new TGStatusBarPart* [npart];
   fParts      = new Int_t [npart];
   fXt         = new Int_t [npart];

   int sz  = 100/npart;
   int tot = 0;
   for (i = 0; i < npart; i++) {
      fStatusPart[i] = new TGStatusBarPart(this, fHeight, fYt);
      AddFrame(fStatusPart[i], fgHints);
      fParts[i] = sz;
      tot += sz;
   }
   if (tot < 100)
      fParts[npart-1] += 100 - tot;
   fNpart = npart;
}

//______________________________________________________________________________
FontStruct_t TGStatusBar::GetDefaultFontStruct()
{
   if (!fgDefaultFont)
      fgDefaultFont = gClient->GetResourcePool()->GetStatusFont();
   return fgDefaultFont->GetFontStruct();
}

//______________________________________________________________________________
const TGGC &TGStatusBar::GetDefaultGC()
{
   if (!fgDefaultGC) {
      fgDefaultGC = new TGGC(*gClient->GetResourcePool()->GetFrameGC());
      fgDefaultGC->SetFont(fgDefaultFont->GetFontHandle());
   }
   return *fgDefaultGC;
}

//______________________________________________________________________________
TGCompositeFrame *TGStatusBar::GetBarPart(Int_t npart) const
{
   // Returns bar part. That allows to put in the bar part
   // something more interesting than text ;-)

   return  ((npart<fNpart) && (npart>=0)) ? (TGCompositeFrame*)fStatusPart[npart] : 0;
}

//______________________________________________________________________________
TGDimension TGStatusBar::GetDefaultSize() const
{
   //

   UInt_t h = fHeight;

   for (int i = 0; i < fNpart; i++) {
      h = TMath::Max(h,fStatusPart[i]->GetDefaultHeight()+1);
   }
   return TGDimension(fWidth, h);
}

//______________________________________________________________________________
void TGStatusBar::SavePrimitive(ofstream &out, Option_t *option)
{
    // Save a status bar widget as a C++ statement(s) on output stream out.

   if (fBackground != GetDefaultFrameBackground()) SaveUserColor(out, option);

   out << endl;
   out << "   // status bar" << endl;

   out << "   TGStatusBar *";
   out << GetName() <<" = new TGStatusBar("<< fParent->GetName()
       << "," << GetWidth() << "," << GetHeight();

   if (fBackground == GetDefaultFrameBackground()) {
      if (GetOptions() == (kSunkenFrame | kHorizontalFrame)) {
           out <<");" << endl;
      } else {
        out << "," << GetOptionString() <<");" << endl;
      }
   } else {
     out << "," << GetOptionString() << ",ucolor);" << endl;
   }

   int i; char quote = '"';

   if (fNpart > 1)  {
      out << "   Int_t parts" << GetName()+5 << "[] = {" << fParts[0];

      for (i=1; i<fNpart; i++) {
           out  << "," << fParts[i];
      }
      out << "};" << endl;

      out << "   " << GetName() << "->SetParts(parts" << GetName()+5
          << "," << fNpart << ");" <<endl;
   }
   for (i=0; i<fNpart; i++) {
      if (fStatusPart[i]->GetText()) {
          out << "   " << GetName() << "->SetText(" << quote
              << fStatusPart[i]->GetText()->GetString()
              << quote << "," << i << ");" << endl;
      } else {
         if (!fStatusPart[i]->GetList()) continue;
         out << "   TGCompositeFrame *" << fStatusPart[i]->GetName()
             << " = " << GetName() << "->GetBarPart(" << i << ");" << endl;

         TGFrameElement *el;
         TIter next(fStatusPart[i]->GetList());

         while ((el = (TGFrameElement *) next())) {
            el->fFrame->SavePrimitive(out, option);
            out << "   " << fStatusPart[i]->GetName() << "->AddFrame("
                << el->fFrame->GetName();
            el->fLayout->SavePrimitive(out, option);
            out << ");" << endl;
         }
      }
   }
}
<commit_msg>Add protections in TGStatusBar::SetParts to avoid an object twice.<commit_after>// @(#)root/gui:$Name:  $:$Id: TGStatusBar.cxx,v 1.8 2003/11/12 19:34:59 rdm Exp $
// Author: Fons Rademakers   23/01/98

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

    This source is based on Xclass95, a Win95-looking GUI toolkit.
    Copyright (C) 1996, 1997 David Barth, Ricky Ralston, Hector Peraza.

    Xclass95 is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

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

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TGStatusBar                                                          //
//                                                                      //
// Provides a StatusBar widget.                                         //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

#include "TGStatusBar.h"
#include "TGResourcePool.h"
#include "Riostream.h"

const TGFont *TGStatusBar::fgDefaultFont = 0;
TGGC         *TGStatusBar::fgDefaultGC = 0;
TGLayoutHints *TGStatusBar::fgHints = new TGLayoutHints(kLHintsTop | kLHintsLeft, 0, 0, 0, 0);

class TGStatusBarPart : public TGHorizontalFrame {
friend class TGStatusBar;
private:
   TGString  *fStatusInfo;    // status text to be displayed in this part
   Int_t      fYt;            // y position of text in frame
   virtual void DoRedraw();

public:
   TGStatusBarPart(const TGWindow *p, Int_t h, Int_t y, ULong_t back = GetDefaultFrameBackground());
   ~TGStatusBarPart() { delete fStatusInfo; DestroyWindow(); }
   void SetText(TGString *text);
   const TGString *GetText() const { return fStatusInfo; }
};

//______________________________________________________________________________
TGStatusBarPart::TGStatusBarPart(const TGWindow *p, Int_t h, Int_t y, ULong_t back)
   : TGHorizontalFrame(p, 5, 5, kChildFrame | kHorizontalFrame, back)
{
   // Create statusbar part frame. This frame will contain the text for this
   // statusbar part.

   fStatusInfo = 0;
   fYt = y + 1;
   fHeight = h;
   MapWindow();
}

//______________________________________________________________________________
void TGStatusBarPart::SetText(TGString *text)
{
   // Set text in this part of the statusbar.

   if (fStatusInfo) delete fStatusInfo;
   fStatusInfo = text;
   fClient->NeedRedraw(this);
}

//______________________________________________________________________________
void TGStatusBarPart::DoRedraw()
{
   // Draw string in statusbar part frame.

   TGHorizontalFrame::DoRedraw();

   if (fStatusInfo)
      fStatusInfo->Draw(fId, TGStatusBar::GetDefaultGC()(), 3, fYt);
}

ClassImp(TGStatusBar)

//______________________________________________________________________________
TGStatusBar::TGStatusBar(const TGWindow *p, UInt_t w, UInt_t h,
                         UInt_t options, ULong_t back) :
   TGHorizontalFrame(p, w, h, options, back)
{
   // Create a status bar widget. By default it consist of one part.
   // Multiple parts can be created using SetParts().

   fBorderWidth   = 2;
   fStatusPart    = new TGStatusBarPart* [1];
   fParts         = new Int_t [1];
   fXt            = new Int_t [1];
   fParts[0]      = 100;
   fNpart         = 1;
   f3DCorner      = kTRUE;

   int max_ascent, max_descent;
   gVirtualX->GetFontProperties(GetDefaultFontStruct(), max_ascent, max_descent);
   int ht = max_ascent + max_descent;

   fYt = max_ascent;

   fStatusPart[0] = new TGStatusBarPart(this, ht, fYt);
   AddFrame(fStatusPart[0], fgHints);
   Resize(w, ht + 5);
}

//______________________________________________________________________________
TGStatusBar::~TGStatusBar()
{
   // Delete status bar widget.

   for (int i = 0; i < fNpart; i++)
      delete fStatusPart[i];

   delete [] fStatusPart;
   delete [] fParts;
   delete [] fXt;
}

//______________________________________________________________________________
void TGStatusBar::SetText(TGString *text, Int_t partidx)
{
   // Set text in partition partidx in status bar. The TGString is
   // adopted by the status bar.

   if (partidx < 0 || partidx >= fNpart) {
      Error("SetText", "partidx out of range (0,%d)", fNpart-1);
      return;
   }

   fStatusPart[partidx]->SetText(text);
}

//______________________________________________________________________________
void TGStatusBar::SetText(const char *text, Int_t partidx)
{
   // Set text in partion partidx in status bar.

   SetText(new TGString(text), partidx);
}

//______________________________________________________________________________
void TGStatusBar::DrawBorder()
{
   // Draw the status bar border (including cute 3d corner).

   // Current width is known at this stage so calculate fXt's.
   int i;
   for (i = 0; i < fNpart; i++) {
      if (i == 0)
         fXt[i] = 0;
      else
         fXt[i] = fXt[i-1] + (fWidth * fParts[i-1] / 100);
   }

   //TGFrame::DrawBorder();
   for (i = 0; i < fNpart; i++) {
      int xmax, xmin = fXt[i];
      if (i == fNpart-1)
         xmax = fWidth;
      else
         xmax = fXt[i+1] - 2;

      if (i == fNpart-1)
         fStatusPart[i]->MoveResize(fXt[i]+2, 1, xmax - fXt[i] - 15, fHeight - 2);
      else
         fStatusPart[i]->MoveResize(fXt[i]+2, 1, xmax - fXt[i] - 4, fHeight - 2);

      gVirtualX->DrawLine(fId, GetShadowGC()(), xmin, 0, xmax-2, 0);
      gVirtualX->DrawLine(fId, GetShadowGC()(), xmin, 0, xmin, fHeight-2);
      gVirtualX->DrawLine(fId, GetHilightGC()(), xmin, fHeight-1, xmax-1, fHeight-1);
      if (i == fNpart-1)
         gVirtualX->DrawLine(fId, GetHilightGC()(), xmax-1, fHeight-1, xmax-1, 0);
      else
         gVirtualX->DrawLine(fId, GetHilightGC()(), xmax-1, fHeight-1, xmax-1, 1);
   }

   // 3d corner...
   if (f3DCorner) {
      gVirtualX->DrawLine(fId, GetShadowGC()(),  fWidth-3,  fHeight-2, fWidth-2, fHeight-3);
      gVirtualX->DrawLine(fId, GetShadowGC()(),  fWidth-4,  fHeight-2, fWidth-2, fHeight-4);
      gVirtualX->DrawLine(fId, GetHilightGC()(), fWidth-5,  fHeight-2, fWidth-2, fHeight-5);

      gVirtualX->DrawLine(fId, GetShadowGC()(),  fWidth-7,  fHeight-2, fWidth-2, fHeight-7);
      gVirtualX->DrawLine(fId, GetShadowGC()(),  fWidth-8,  fHeight-2, fWidth-2, fHeight-8);
      gVirtualX->DrawLine(fId, GetHilightGC()(), fWidth-9,  fHeight-2, fWidth-2, fHeight-9);

      gVirtualX->DrawLine(fId, GetShadowGC()(),  fWidth-11, fHeight-2, fWidth-2, fHeight-11);
      gVirtualX->DrawLine(fId, GetShadowGC()(),  fWidth-12, fHeight-2, fWidth-2, fHeight-12);
      gVirtualX->DrawLine(fId, GetHilightGC()(), fWidth-13, fHeight-2, fWidth-2, fHeight-13);

      gVirtualX->DrawLine(fId, GetBckgndGC()(),  fWidth-13, fHeight-1, fWidth-1, fHeight-1);
      gVirtualX->DrawLine(fId, GetBckgndGC()(),  fWidth-1,  fHeight-1, fWidth-1, fHeight-13);
   }
}

//______________________________________________________________________________
void TGStatusBar::DoRedraw()
{
   // Redraw status bar.

   // calls DrawBorder()
   TGFrame::DoRedraw();

   for (int i = 0; i < fNpart; i++)
      fStatusPart[i]->DoRedraw();
}

//______________________________________________________________________________
void TGStatusBar::SetParts(Int_t *parts, Int_t npart)
{
   // Divide the status bar in nparts. Size of each part is given in parts
   // array (percentual).

   if (npart < 1) {
      Warning("SetParts", "must be at least one part");
      npart = 1;
   }
   if (npart > 15) {
      Error("SetParts", "to many parts (limit is 15)");
      return;
   }

   int i;
   for (i = 0; i < fNpart; i++) {
      fList->Remove(fStatusPart[i]);
      delete fStatusPart[i];
   }

   delete [] fStatusPart;
   delete [] fParts;
   delete [] fXt;
   fList->Delete();

   fStatusPart = new TGStatusBarPart* [npart];
   fParts      = new Int_t [npart];
   fXt         = new Int_t [npart];

   int tot = 0;
   for (i = 0; i < npart; i++) {
      fStatusPart[i] = new TGStatusBarPart(this, fHeight, fYt);
      AddFrame(fStatusPart[i], fgHints);
      fParts[i] = parts[i];
      tot += parts[i];
      if (tot > 100)
         Error("SetParts", "sum of part > 100");
   }
   if (tot < 100)
      fParts[npart-1] += 100 - tot;
   fNpart = npart;
}

//______________________________________________________________________________
void TGStatusBar::SetParts(Int_t npart)
{
   // Divide the status bar in npart equal sized parts.

   if (npart < 1) {
      Warning("SetParts", "must be at least one part");
      npart = 1;
   }
   if (npart > 40) {
      Error("SetParts", "to many parts (limit is 40)");
      return;
   }

   int i;
   for (i = 0; i < fNpart; i++) {
      fList->Remove(fStatusPart[i]);
      delete fStatusPart[i];
   }

   delete [] fStatusPart;
   delete [] fParts;
   delete [] fXt;
   fList->Delete();

   fStatusPart = new TGStatusBarPart* [npart];
   fParts      = new Int_t [npart];
   fXt         = new Int_t [npart];

   int sz  = 100/npart;
   int tot = 0;
   for (i = 0; i < npart; i++) {
      fStatusPart[i] = new TGStatusBarPart(this, fHeight, fYt);
      AddFrame(fStatusPart[i], fgHints);
      fParts[i] = sz;
      tot += sz;
   }
   if (tot < 100)
      fParts[npart-1] += 100 - tot;
   fNpart = npart;
}

//______________________________________________________________________________
FontStruct_t TGStatusBar::GetDefaultFontStruct()
{
   if (!fgDefaultFont)
      fgDefaultFont = gClient->GetResourcePool()->GetStatusFont();
   return fgDefaultFont->GetFontStruct();
}

//______________________________________________________________________________
const TGGC &TGStatusBar::GetDefaultGC()
{
   if (!fgDefaultGC) {
      fgDefaultGC = new TGGC(*gClient->GetResourcePool()->GetFrameGC());
      fgDefaultGC->SetFont(fgDefaultFont->GetFontHandle());
   }
   return *fgDefaultGC;
}

//______________________________________________________________________________
TGCompositeFrame *TGStatusBar::GetBarPart(Int_t npart) const
{
   // Returns bar part. That allows to put in the bar part
   // something more interesting than text ;-)

   return  ((npart<fNpart) && (npart>=0)) ? (TGCompositeFrame*)fStatusPart[npart] : 0;
}

//______________________________________________________________________________
TGDimension TGStatusBar::GetDefaultSize() const
{
   //

   UInt_t h = fHeight;

   for (int i = 0; i < fNpart; i++) {
      h = TMath::Max(h,fStatusPart[i]->GetDefaultHeight()+1);
   }
   return TGDimension(fWidth, h);
}

//______________________________________________________________________________
void TGStatusBar::SavePrimitive(ofstream &out, Option_t *option)
{
    // Save a status bar widget as a C++ statement(s) on output stream out.

   if (fBackground != GetDefaultFrameBackground()) SaveUserColor(out, option);

   out << endl;
   out << "   // status bar" << endl;

   out << "   TGStatusBar *";
   out << GetName() <<" = new TGStatusBar("<< fParent->GetName()
       << "," << GetWidth() << "," << GetHeight();

   if (fBackground == GetDefaultFrameBackground()) {
      if (GetOptions() == (kSunkenFrame | kHorizontalFrame)) {
           out <<");" << endl;
      } else {
        out << "," << GetOptionString() <<");" << endl;
      }
   } else {
     out << "," << GetOptionString() << ",ucolor);" << endl;
   }

   int i; char quote = '"';

   if (fNpart > 1)  {
      out << "   Int_t parts" << GetName()+5 << "[] = {" << fParts[0];

      for (i=1; i<fNpart; i++) {
           out  << "," << fParts[i];
      }
      out << "};" << endl;

      out << "   " << GetName() << "->SetParts(parts" << GetName()+5
          << "," << fNpart << ");" <<endl;
   }
   for (i=0; i<fNpart; i++) {
      if (fStatusPart[i]->GetText()) {
          out << "   " << GetName() << "->SetText(" << quote
              << fStatusPart[i]->GetText()->GetString()
              << quote << "," << i << ");" << endl;
      } else {
         if (!fStatusPart[i]->GetList()) continue;
         out << "   TGCompositeFrame *" << fStatusPart[i]->GetName()
             << " = " << GetName() << "->GetBarPart(" << i << ");" << endl;

         TGFrameElement *el;
         TIter next(fStatusPart[i]->GetList());

         while ((el = (TGFrameElement *) next())) {
            el->fFrame->SavePrimitive(out, option);
            out << "   " << fStatusPart[i]->GetName() << "->AddFrame("
                << el->fFrame->GetName();
            el->fLayout->SavePrimitive(out, option);
            out << ");" << endl;
         }
      }
   }
}
<|endoftext|>


<commit_before>//===========================================
//  Lumina-DE source code
//  Copyright (c) 2015, Ken Moore
//  Available under the 3-clause BSD license
//  See the LICENSE file for full details
//===========================================
#include "TerminalWidget.h"

#include <QProcessEnvironment>
#include <QDebug>
#include <QApplication>
#include <QScrollBar>

TerminalWidget::TerminalWidget(QWidget *parent, QString dir) : QTextEdit(parent){
  //Setup the text widget
  this->setLineWrapMode(QTextEdit::WidgetWidth);
  //this->setReadOnly(true); //the key event catch will do the process/widget forwarding
  //this->setPlainText("WARNING: This utility is still incomplete and does not function properly yet");

  //Create/open the TTY port
  PROC = new TTYProcess(this);
  qDebug() << "Open new TTY";
  //int fd;
  bool ok = PROC->startTTY( QProcessEnvironment::systemEnvironment().value("SHELL","/bin/sh") );
  qDebug() << " - opened:" << ok;
  this->setEnabled(PROC->isOpen());

  //Connect the signals/slots
  connect(PROC, SIGNAL(readyRead()), this, SLOT(UpdateText()) );
  connect(PROC, SIGNAL(processClosed()), this, SLOT(ShellClosed()) );

}

TerminalWidget::~TerminalWidget(){
  aboutToClose();
}

void TerminalWidget::aboutToClose(){
  if(PROC->isOpen()){ PROC->closeTTY(); } //TTY PORT
}

// ==================
//          PRIVATE
// ==================
void TerminalWidget::applyData(QByteArray data){
  //Quick global replacement (this widget reads both as newlines)
  data = data.replace("\r\n","\n");
  //Iterate through the data and apply it when possible
  for(int i=0; i<data.size(); i++){
    if( data.at(i)=='\b' ){
      //Simple Backspace
      this->textCursor().deletePreviousChar();

    }else if( data.at(i)=='\x1B' ){
      //ANSI Control Code start
      //Look for the end of the code
      int end = -1;
      for(int j=1; j<(data.size()-i) && end<0; j++){
        if(QChar(data.at(i+j)).isLetter()){ end = j; }
      }
      if(end<0){ return; } //skip everything else - no end to code found
      applyANSI(data.mid(i, end));
      i+=end; //move the final loop along - already handled these bytes

    }else{
      //Plaintext character - just add it here
      this->insertPlainText( QChar(data.at(i)) );
    }

  } //end loop over data
}

void TerminalWidget::applyANSI(QByteArray code){
  //Note: the first byte is often the "[" character
  //CURSOR MOVEMENT
  if( code.endsWith("A") ){ //Move Up
    int num = 1;
    if(code.size()>2){ num = code.mid(1, code.size()-2).toInt(); } //everything in the middle
    this->textCursor().movePosition(QTextCursor::Up, QTextCursor::MoveAnchor, num);
  }else if(code.endsWith("B")){ //Move Down
    int num = 1;
    if(code.size()>2){ num = code.mid(1, code.size()-2).toInt(); } //everything in the middle
    this->textCursor().movePosition(QTextCursor::Down, QTextCursor::MoveAnchor, num);
  }else if(code.endsWith("C")){ //Move Forward
    int num = 1;
    if(code.size()>2){ num = code.mid(1, code.size()-2).toInt(); } //everything in the middle
    for(int i=0; i<num; i++){ this->moveCursor(QTextCursor::Right, QTextCursor::MoveAnchor); }
    //this->textCursor().movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, num);
  }else if(code.endsWith("D")){ //Move Back
    int num = 1;
    if(code.size()>2){ num = code.mid(1, code.size()-2).toInt(); } //everything in the middle
    for(int i=0; i<num; i++){ this->moveCursor(QTextCursor::Left, QTextCursor::MoveAnchor); }
    //this->textCursor().movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, num);
  }else if(code.endsWith("E")){ //Move Next/down Lines (go to beginning)
    int num = 1;
    if(code.size()>2){ num = code.mid(1, code.size()-2).toInt(); } //everything in the middle
    this->textCursor().movePosition(QTextCursor::NextRow, QTextCursor::MoveAnchor, num);
  }else if(code.endsWith("F")){ //Move Previous/up Lines (go to beginning)
    int num = 1;
    if(code.size()>2){ num = code.mid(1, code.size()-2).toInt(); } //everything in the middle
    this->textCursor().movePosition(QTextCursor::PreviousRow, QTextCursor::MoveAnchor, num);
  }else if(code.endsWith("G")){ //Move to specific column
    int num = 1;
    if(code.size()>2){ num = code.mid(1, code.size()-2).toInt(); } //everything in the middle
    this->textCursor().setPosition(num);
  }else if(code.endsWith("H")){ //Move to specific position (row/column)
    int mid = code.indexOf(";");
    if(mid>0){
      int numR, numC; numR = numC = 1;
      if(mid >=3){ numR = code.mid(1,mid-1).toInt(); }
      if(mid < code.size()-1){ numC = code.mid(mid+1,code.size()-mid-1).toInt(); }
      //this->textCursor().setPosition(
      qDebug() << "Set Text Position (absolute):" << "Row:" << numR << "Col:" << numC;
      // TO-DO
    }
  }else if(code.endsWith("J")){ //ED - Erase Display

  }
}

//Outgoing Data parsing
void TerminalWidget::sendKeyPress(int key){
  QByteArray ba;
  //Check for special keys
  switch(key){
    case Qt::Key_Delete:
	ba.append("\x7F");
        break;
    case Qt::Key_Backspace:
	ba.append("\x08");
        break;
    case Qt::Key_Left:
	ba.append("\x1b[D");
        break;
    case Qt::Key_Right:
	ba.append("\x1b[C");
        break;
    case Qt::Key_Up:
        ba.append("\x1b[A");
        break;
    case Qt::Key_Down:
        ba.append("\x1b[B");
        break;
  }

  //qDebug() << "Forward Input:" << txt << ev->key() << ba;
  if(!ba.isEmpty()){ PROC->writeTTY(ba); }
}

// ==================
//    PRIVATE SLOTS
// ==================
void TerminalWidget::UpdateText(){
  //read the data from the process
  //qDebug() << "UpdateText";
  if(!PROC->isOpen()){ return; }
  applyData(PROC->readTTY());
  //adjust the scrollbar as needed
  this->verticalScrollBar()->setValue(this->verticalScrollBar()->maximum());
}

void TerminalWidget::ShellClosed(){
  emit ProcessClosed(this->whatsThis());
}

// ==================
//       PROTECTED
// ==================
void TerminalWidget::keyPressEvent(QKeyEvent *ev){

  if(ev->text().isEmpty() || ev->text()=="\b" ){
    sendKeyPress(ev->key());
  }else{
    QByteArray ba; ba.append(ev->text()); //avoid any byte conversions
    PROC->writeTTY(ba);
  }

  ev->ignore();
}

void TerminalWidget::mousePressEvent(QMouseEvent *ev){
  this->setFocus();
  Q_UNUSED(ev);
}

void TerminalWidget::mouseDoubleClickEvent(QMouseEvent *ev){
  Q_UNUSED(ev);
}

void TerminalWidget::contextMenuEvent(QContextMenuEvent *ev){
  Q_UNUSED(ev);
}

void TerminalWidget::resizeEvent(QResizeEvent *ev){
  if(!PROC->isOpen()){ return; }
  QSize pix = ev->size(); //pixels
  QSize chars;
    chars.setWidth( pix.width()/this->fontMetrics().width("W") );
    chars.setHeight( pix.height()/this->fontMetrics().lineSpacing() );

  PROC->setTerminalSize(chars,pix);
  QTextEdit::resizeEvent(ev);
}
<commit_msg>Get the left/right arrow keys working properly.<commit_after>//===========================================
//  Lumina-DE source code
//  Copyright (c) 2015, Ken Moore
//  Available under the 3-clause BSD license
//  See the LICENSE file for full details
//===========================================
#include "TerminalWidget.h"

#include <QProcessEnvironment>
#include <QDebug>
#include <QApplication>
#include <QScrollBar>

TerminalWidget::TerminalWidget(QWidget *parent, QString dir) : QTextEdit(parent){
  //Setup the text widget
  this->setLineWrapMode(QTextEdit::WidgetWidth);
  //this->setReadOnly(true); //the key event catch will do the process/widget forwarding
  //this->setPlainText("WARNING: This utility is still incomplete and does not function properly yet");

  //Create/open the TTY port
  PROC = new TTYProcess(this);
  qDebug() << "Open new TTY";
  //int fd;
  bool ok = PROC->startTTY( QProcessEnvironment::systemEnvironment().value("SHELL","/bin/sh") );
  qDebug() << " - opened:" << ok;
  this->setEnabled(PROC->isOpen());

  //Connect the signals/slots
  connect(PROC, SIGNAL(readyRead()), this, SLOT(UpdateText()) );
  connect(PROC, SIGNAL(processClosed()), this, SLOT(ShellClosed()) );

}

TerminalWidget::~TerminalWidget(){
  aboutToClose();
}

void TerminalWidget::aboutToClose(){
  if(PROC->isOpen()){ PROC->closeTTY(); } //TTY PORT
}

// ==================
//          PRIVATE
// ==================
void TerminalWidget::applyData(QByteArray data){
  //Quick global replacement (this widget reads both as newlines)
  data = data.replace("\r\n","\n");
  //Iterate through the data and apply it when possible
  for(int i=0; i<data.size(); i++){
    if( data.at(i)=='\b' ){
      //Simple cursor backward 1 (NOT backspace in this context!!)
      //this->textCursor().deletePreviousChar();
      this->moveCursor(QTextCursor::Left, QTextCursor::MoveAnchor);
    }else if( data.at(i)=='\x1B' ){
      //ANSI Control Code start
      //Look for the end of the code
      int end = -1;
      for(int j=1; j<(data.size()-i) && end<0; j++){
        if(QChar(data.at(i+j)).isLetter()){ end = j; }
      }
      if(end<0){ return; } //skip everything else - no end to code found
      applyANSI(data.mid(i+1, end));
      i+=end; //move the final loop along - already handled these bytes

    }else{
      //Special Check: if inserting text within a line, clear the rest of this line first
      if(i==0 && this->textCursor().position() < this->document()->characterCount()-1){
        applyANSI("[K");
      }
      //Plaintext character - just add it here
      this->insertPlainText( QChar(data.at(i)) );
    }

  } //end loop over data
}

void TerminalWidget::applyANSI(QByteArray code){
  //Note: the first byte is often the "[" character
  //qDebug() << "Handle ANSI:" << code;
  //CURSOR MOVEMENT
  if( code.endsWith("A") ){ //Move Up
    int num = 1;
    if(code.size()>2){ num = code.mid(1, code.size()-2).toInt(); } //everything in the middle
    this->textCursor().movePosition(QTextCursor::Up, QTextCursor::MoveAnchor, num);
  }else if(code.endsWith("B")){ //Move Down
    int num = 1;
    if(code.size()>2){ num = code.mid(1, code.size()-2).toInt(); } //everything in the middle
    this->textCursor().movePosition(QTextCursor::Down, QTextCursor::MoveAnchor, num);
  }else if(code.endsWith("C")){ //Move Forward
    int num = 1;
    if(code.size()>2){ num = code.mid(1, code.size()-2).toInt(); } //everything in the middle
    for(int i=0; i<num; i++){ this->moveCursor(QTextCursor::Right, QTextCursor::MoveAnchor); }
    //this->textCursor().movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, num);
  }else if(code.endsWith("D")){ //Move Back
    int num = 1;
    if(code.size()>2){ num = code.mid(1, code.size()-2).toInt(); } //everything in the middle
    for(int i=0; i<num; i++){ this->moveCursor(QTextCursor::Left, QTextCursor::MoveAnchor); }
    //this->textCursor().movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, num);
  }else if(code.endsWith("E")){ //Move Next/down Lines (go to beginning)
    int num = 1;
    if(code.size()>2){ num = code.mid(1, code.size()-2).toInt(); } //everything in the middle
    this->textCursor().movePosition(QTextCursor::NextRow, QTextCursor::MoveAnchor, num);
  }else if(code.endsWith("F")){ //Move Previous/up Lines (go to beginning)
    int num = 1;
    if(code.size()>2){ num = code.mid(1, code.size()-2).toInt(); } //everything in the middle
    this->textCursor().movePosition(QTextCursor::PreviousRow, QTextCursor::MoveAnchor, num);
  }else if(code.endsWith("G")){ //Move to specific column
    int num = 1;
    if(code.size()>2){ num = code.mid(1, code.size()-2).toInt(); } //everything in the middle
    this->textCursor().setPosition(num);
  }else if(code.endsWith("H") || code.endsWith("f") ){ //Move to specific position (row/column)
    int mid = code.indexOf(";");
    if(mid>0){
      int numR, numC; numR = numC = 1;
      if(mid >=3){ numR = code.mid(1,mid-1).toInt(); }
      if(mid < code.size()-1){ numC = code.mid(mid+1,code.size()-mid-1).toInt(); }
      //this->textCursor().setPosition(
      qDebug() << "Set Text Position (absolute):" << "Row:" << numR << "Col:" << numC;
      // TO-DO
    }

   // DISPLAY CLEAR CODES
  }else if(code.endsWith("J")){ //ED - Erase Display

  }else if(code.endsWith("K")){ //EL - Erase in Line
    int num = 0;
    if(code.size()>2){ num = code.mid(1, code.size()-2).toInt(); } //everything in the middle
    qDebug() << "Erase Number" << num;
    //Now determine what should be cleared based on code
    if(num==1){

    }else if(num==2){

    }else{
      //Clear from current cursor to end of line
      for(int i=this->textCursor().position(); i<this->document()->characterCount()+1; i++){
      //while(this->document()->characterAt(this->textCursor().position())!=QChar('\n') && this->textCursor().position() < this->document()->characterCount()){
	if(this->document()->characterAt(this->textCursor().position())=='\n'){ break; }
        this->textCursor().deleteChar();
      }
    }
  }
}

//Outgoing Data parsing
void TerminalWidget::sendKeyPress(int key){
  QByteArray ba;
  //Check for special keys
  switch(key){
    case Qt::Key_Delete:
	ba.append("\x7F");
        break;
    case Qt::Key_Backspace:
	ba.append("\x08");
        break;
    case Qt::Key_Left:
	ba.append("\x1b[D");
        break;
    case Qt::Key_Right:
	ba.append("\x1b[C");
        break;
    case Qt::Key_Up:
        ba.append("\x1b[A");
        break;
    case Qt::Key_Down:
        ba.append("\x1b[B");
        break;
  }

  //qDebug() << "Forward Input:" << txt << ev->key() << ba;
  if(!ba.isEmpty()){ PROC->writeTTY(ba); }
}

// ==================
//    PRIVATE SLOTS
// ==================
void TerminalWidget::UpdateText(){
  //read the data from the process
  //qDebug() << "UpdateText";
  if(!PROC->isOpen()){ return; }
  applyData(PROC->readTTY());
  //adjust the scrollbar as needed
  this->verticalScrollBar()->setValue(this->verticalScrollBar()->maximum());
}

void TerminalWidget::ShellClosed(){
  emit ProcessClosed(this->whatsThis());
}

// ==================
//       PROTECTED
// ==================
void TerminalWidget::keyPressEvent(QKeyEvent *ev){

  if(ev->text().isEmpty() || ev->text()=="\b" ){
    sendKeyPress(ev->key());
  }else{
    QByteArray ba; ba.append(ev->text()); //avoid any byte conversions
    PROC->writeTTY(ba);
  }

  ev->ignore();
}

void TerminalWidget::mousePressEvent(QMouseEvent *ev){
  this->setFocus();
  Q_UNUSED(ev);
}

void TerminalWidget::mouseDoubleClickEvent(QMouseEvent *ev){
  Q_UNUSED(ev);
}

void TerminalWidget::contextMenuEvent(QContextMenuEvent *ev){
  Q_UNUSED(ev);
}

void TerminalWidget::resizeEvent(QResizeEvent *ev){
  if(!PROC->isOpen()){ return; }
  QSize pix = ev->size(); //pixels
  QSize chars;
    chars.setWidth( pix.width()/this->fontMetrics().width("W") );
    chars.setHeight( pix.height()/this->fontMetrics().lineSpacing() );

  PROC->setTerminalSize(chars,pix);
  QTextEdit::resizeEvent(ev);
}
<|endoftext|>


<commit_before>#include "search.chtml.h"
#include "dllInterface.h"
#include "tabTables.h"
#include "Spell.h"

#include <string.h>
#include <stdlib.h>
#include <string>
#include <sstream>
#include "win32.h"

using namespace std;

enum TableId {
	ZONE,
	SPELL,
};
enum TableRowId {
	NAME = ENTRY+1,
};

extern "C"
void getResponse(const char* urlPart, DllResponseData* drd) {
	WorldMapAreas_ensureLoad();
	AreaTable_ensureLoad();
	gSpells.load();

	searchChtml context;
	context.urlPart = urlPart;

	string searchString = toupper(urlPart);

	{
	Table t;
	t.id = ZONE;
	t.title = "Zones";
	Column c = { NAME, "Name", false, true, ENTRY, "zone" };
	t.columns.push_back(c);

	for(AreaTable::citr itr = gAreaTable.begin(); itr != gAreaTable.end(); ++itr) {
		const Area& a(itr->second);
		if(a.parent == 0 &&
			strcasestr(a.name, urlPart))
		{
			Row r;
			r[ENTRY] = toString(itr->first);
			r[NAME] = a.name;
			t.array.push_back(r);
		}
	}
	context.mTables.push_back(t);
	}
	{
		Table t;
		t.id = SPELL;
		t.title = "Spells";
		Column c = { NAME, "Name", false, true, ENTRY, "spell" };
		t.columns.push_back(c);
		for(Spells::citr itr = gSpells.begin(); itr != gSpells.end(); ++itr) {
			const Spell& s(itr->second);
			if(strcasestr(s.name, urlPart))
			{
				Row r;
				r[ENTRY] = toString(itr->first);
				r[NAME] = s.name;
				t.array.push_back(r);
			}
		}
		context.mTables.push_back(t);
	}

	getResponse(drd, context);
}
<commit_msg>Search: limit number of results.<commit_after>#include "search.chtml.h"
#include "dllInterface.h"
#include "tabTables.h"
#include "Spell.h"

#include <string.h>
#include <stdlib.h>
#include <string>
#include <sstream>
#include "win32.h"

using namespace std;

enum TableId {
	ZONE,
	SPELL,
};
enum TableRowId {
	NAME = ENTRY+1,
};
#define MAX_COUNT 100

extern "C"
void getResponse(const char* urlPart, DllResponseData* drd) {
	WorldMapAreas_ensureLoad();
	AreaTable_ensureLoad();
	gSpells.load();

	searchChtml context;
	context.urlPart = urlPart;

	string searchString = toupper(urlPart);

	{
	Table t;
	t.id = ZONE;
	t.title = "Zones";
	Column c = { NAME, "Name", false, true, ENTRY, "zone" };
	t.columns.push_back(c);

	for(AreaTable::citr itr = gAreaTable.begin(); itr != gAreaTable.end(); ++itr) {
		const Area& a(itr->second);
		if(a.parent == 0 &&
			strcasestr(a.name, urlPart))
		{
			Row r;
			r[ENTRY] = toString(itr->first);
			r[NAME] = a.name;
			t.array.push_back(r);
		}
	}
	context.mTables.push_back(t);
	}
	{
		Table t;
		t.id = SPELL;
		t.title = "Spells";
		Column c = { NAME, "Name", false, true, ENTRY, "spell" };
		t.columns.push_back(c);
		for(Spells::citr itr = gSpells.begin();
			itr != gSpells.end() && t.array.size() < MAX_COUNT;
			++itr)
		{
			const Spell& s(itr->second);
			if(strcasestr(s.name, urlPart))
			{
				Row r;
				r[ENTRY] = toString(itr->first);
				r[NAME] = s.name;
				t.array.push_back(r);
			}
		}
		context.mTables.push_back(t);
	}

	getResponse(drd, context);
}
<|endoftext|>


<commit_before>
#pragma once

#include <sstream>
#include <type_traits>

#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>

#include <reflectionzeug/property/Property.h>

namespace reflectionzeug
{

/**
*  @brief
*    Convert glm vector into string
*
*  @param[in] data
*    Pointer to vector data
*
*  @return
*    String representation
*/
template <typename T, unsigned Size>
std::string glmToString(const T * data)
{
    std::stringstream ss;

    ss << "(";

    for (unsigned i = 0; i < Size; ++i)
    {
        if (i > 0)
            ss << ", ";

        ss << data[i];
    }

    ss << ")";

    return ss.str();
}

/**
*  @brief
*    Convert string into glm vector
*
*  @param[in] string
*    String representation
*  @param[in] data
*    Pointer to vector data
*
*  @return
*    'true' if all went fine, else 'false'
*/
template <typename T, unsigned Size>
bool glmFromString(const std::string & string, T * data)
{
    std::string elementRegex = std::is_integral<T>::value ? "(-|\\+)?\\d+" : "(-|\\+)?\\d+\\.?\\d*";

    std::stringstream ss;
    ss << "\\s*\\(";
    for (unsigned i=0; i<Size-1; ++i)
    {
        ss << "(" << elementRegex << ")";
        ss << "\\s*,\\s*";
    }
    ss << elementRegex << "\\)\\s*";

    if (!reflectionzeug::util::matchesRegex(string, ss.str()))
        return false;

    std::vector<std::string> parts = reflectionzeug::util::extract(string, elementRegex);

    assert(parts.size() == Size);

    for (unsigned i = 0; i < Size; ++i)
    {
        const std::string & part = parts[i];
        data[i] = std::is_integral<T>::value ? static_cast<T>(std::stoi(part)) : static_cast<T>(std::stod(part));
    }

    return true;
}

/**
*  @brief
*    Property implementation for GLM vector types
*/
template <typename VectorType, typename ValueType, unsigned Size>
class PropertyGlmVec : public reflectionzeug::AbstractValueProperty<VectorType>
{
public:
    /**
    *  @brief
    *    Constructor
    */
    template <typename... Arguments>
    PropertyGlmVec(Arguments&&... args)
    : reflectionzeug::AbstractValueProperty<VectorType>(std::forward<Arguments>(args)...)
    {
    }

    // Virtual AbstractProperty interface
    virtual std::string toString() const override
    {
        return glmToString<ValueType, Size>(glm::value_ptr(this->value()));
    }

    virtual bool fromString(const std::string & string) override
    {
        VectorType value;
        if (!glmFromString<ValueType, Size>(string, glm::value_ptr(value)))
            return false;

        this->setValue(value);
        return true;
    }

    virtual Variant toVariant() const override
    {
        // Return variant array
        VectorType vector = this->value();
        Variant array = Variant::array();
        for (size_t i=0; i<Size; i++) {
            array.toArray()->push_back(vector[i]);
        }
        return array;
    }

    virtual bool fromVariant(const Variant & value) override
    {
        // Read from variant of the exact type
        if (value.hasType<VectorType>() || value.canConvert<VectorType>()) {
            this->setValue( value.value<VectorType>() );
            return true;
        }

        // Read from string
        else if (value.hasType<std::string>() || value.canConvert<std::string>()) {
            this->fromString( value.value<std::string>() );
            return true;
        }

        // Read from variant array
        else if (value.hasType<VariantArray>()) {
            const VariantArray & array = *(value.toArray());
            VectorType vector;
            for (size_t i=0; i<Size; i++) {
                vector[i] = array[i].value<ValueType>();
            }
            this->setValue(vector);
            return true;
        }

        // Invalid value
        return false;
    }
};

/**
*  @brief
*    Property selector for properties of type glm::vec2
*/
template <>
struct PropertyTypeSelector<glm::vec2>
{
    using Type = PropertyGlmVec<glm::vec2, glm::vec2::value_type, 2>;
};

/**
*  @brief
*    Property selector for properties of type glm::ivec2
*/
template <>
struct PropertyTypeSelector<glm::ivec2>
{
    using Type = PropertyGlmVec<glm::ivec2, glm::ivec2::value_type, 2>;
};

/**
*  @brief
*    Property selector for properties of type glm::vec3
*/
template <>
struct PropertyTypeSelector<glm::vec3>
{
    using Type = PropertyGlmVec<glm::vec3, glm::vec3::value_type, 3>;
};

/**
*  @brief
*    Property selector for properties of type glm::ivec3
*/
template <>
struct PropertyTypeSelector<glm::ivec3>
{
    using Type = PropertyGlmVec<glm::ivec3, glm::ivec3::value_type, 3>;
};

/**
*  @brief
*    Property selector for properties of type glm::ivec4
*/
template <>
struct PropertyTypeSelector<glm::vec4>
{
    using Type = PropertyGlmVec<glm::vec4, glm::vec4::value_type, 4>;
};

/**
*  @brief
*    Property selector for properties of type glm::ivec4
*/
template <>
struct PropertyTypeSelector<glm::ivec4>
{
    using Type = PropertyGlmVec<glm::ivec4, glm::ivec4::value_type, 4>;
};

} // namespace reflectionzeug
<commit_msg>Add missing accept() for GLM properties<commit_after>
#pragma once

#include <sstream>
#include <type_traits>

#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>

#include <reflectionzeug/property/Property.h>

namespace reflectionzeug
{

/**
*  @brief
*    Convert glm vector into string
*
*  @param[in] data
*    Pointer to vector data
*
*  @return
*    String representation
*/
template <typename T, unsigned Size>
std::string glmToString(const T * data)
{
    std::stringstream ss;

    ss << "(";

    for (unsigned i = 0; i < Size; ++i)
    {
        if (i > 0)
            ss << ", ";

        ss << data[i];
    }

    ss << ")";

    return ss.str();
}

/**
*  @brief
*    Convert string into glm vector
*
*  @param[in] string
*    String representation
*  @param[in] data
*    Pointer to vector data
*
*  @return
*    'true' if all went fine, else 'false'
*/
template <typename T, unsigned Size>
bool glmFromString(const std::string & string, T * data)
{
    std::string elementRegex = std::is_integral<T>::value ? "(-|\\+)?\\d+" : "(-|\\+)?\\d+\\.?\\d*";

    std::stringstream ss;
    ss << "\\s*\\(";
    for (unsigned i=0; i<Size-1; ++i)
    {
        ss << "(" << elementRegex << ")";
        ss << "\\s*,\\s*";
    }
    ss << elementRegex << "\\)\\s*";

    if (!reflectionzeug::util::matchesRegex(string, ss.str()))
        return false;

    std::vector<std::string> parts = reflectionzeug::util::extract(string, elementRegex);

    assert(parts.size() == Size);

    for (unsigned i = 0; i < Size; ++i)
    {
        const std::string & part = parts[i];
        data[i] = std::is_integral<T>::value ? static_cast<T>(std::stoi(part)) : static_cast<T>(std::stod(part));
    }

    return true;
}

/**
*  @brief
*    Property implementation for GLM vector types
*/
template <typename VectorType, typename ValueType, unsigned Size>
class PropertyGlmVec : public reflectionzeug::AbstractValueProperty<VectorType>
{
public:
    /**
    *  @brief
    *    Constructor
    */
    template <typename... Arguments>
    PropertyGlmVec(Arguments&&... args)
    : reflectionzeug::AbstractValueProperty<VectorType>(std::forward<Arguments>(args)...)
    {
    }

    // Virtual AbstractProperty interface
    virtual std::string toString() const override
    {
        return glmToString<ValueType, Size>(glm::value_ptr(this->value()));
    }

    virtual bool fromString(const std::string & string) override
    {
        VectorType value;
        if (!glmFromString<ValueType, Size>(string, glm::value_ptr(value)))
            return false;

        this->setValue(value);
        return true;
    }

    virtual Variant toVariant() const override
    {
        // Return variant array
        VectorType vector = this->value();
        Variant array = Variant::array();
        for (size_t i=0; i<Size; i++) {
            array.toArray()->push_back(vector[i]);
        }
        return array;
    }

    virtual bool fromVariant(const Variant & value) override
    {
        // Read from variant of the exact type
        if (value.hasType<VectorType>() || value.canConvert<VectorType>()) {
            this->setValue( value.value<VectorType>() );
            return true;
        }

        // Read from string
        else if (value.hasType<std::string>() || value.canConvert<std::string>()) {
            this->fromString( value.value<std::string>() );
            return true;
        }

        // Read from variant array
        else if (value.hasType<VariantArray>()) {
            const VariantArray & array = *(value.toArray());
            VectorType vector;
            for (size_t i=0; i<Size; i++) {
                vector[i] = array[i].value<ValueType>();
            }
            this->setValue(vector);
            return true;
        }

        // Invalid value
        return false;
    }

    virtual void accept(AbstractVisitor * visitor) override
    {
        visitor->callVisitor< PropertyGlmVec<VectorType, ValueType, Size> >(this);
    }
};

/**
*  @brief
*    Property selector for properties of type glm::vec2
*/
template <>
struct PropertyTypeSelector<glm::vec2>
{
    using Type = PropertyGlmVec<glm::vec2, glm::vec2::value_type, 2>;
};

/**
*  @brief
*    Property selector for properties of type glm::ivec2
*/
template <>
struct PropertyTypeSelector<glm::ivec2>
{
    using Type = PropertyGlmVec<glm::ivec2, glm::ivec2::value_type, 2>;
};

/**
*  @brief
*    Property selector for properties of type glm::vec3
*/
template <>
struct PropertyTypeSelector<glm::vec3>
{
    using Type = PropertyGlmVec<glm::vec3, glm::vec3::value_type, 3>;
};

/**
*  @brief
*    Property selector for properties of type glm::ivec3
*/
template <>
struct PropertyTypeSelector<glm::ivec3>
{
    using Type = PropertyGlmVec<glm::ivec3, glm::ivec3::value_type, 3>;
};

/**
*  @brief
*    Property selector for properties of type glm::ivec4
*/
template <>
struct PropertyTypeSelector<glm::vec4>
{
    using Type = PropertyGlmVec<glm::vec4, glm::vec4::value_type, 4>;
};

/**
*  @brief
*    Property selector for properties of type glm::ivec4
*/
template <>
struct PropertyTypeSelector<glm::ivec4>
{
    using Type = PropertyGlmVec<glm::ivec4, glm::ivec4::value_type, 4>;
};

} // namespace reflectionzeug
<|endoftext|>


<commit_before>////////////////////////////////////////////////////////////////////////////
//	Module 		: script_engine_script.cpp
//	Created 	: 25.12.2002
//  Modified 	: 13.05.2004
//	Author		: Dmitriy Iassenev
//	Description : ALife Simulator script engine export
////////////////////////////////////////////////////////////////////////////

#include "pch_script.h"
#include "script_engine.h"
#include "ai_space.h"
#include "script_debugger.h"

using namespace luabind;

void LuaLog(LPCSTR caMessage)
{
#ifndef MASTER_GOLD
	ai().script_engine().script_log	(ScriptStorage::eLuaMessageTypeMessage,"%s",caMessage);
#endif // #ifndef MASTER_GOLD

#ifdef USE_DEBUGGER
#	ifndef USE_LUA_STUDIO
		if( ai().script_engine().debugger() )
			ai().script_engine().debugger()->Write(caMessage);
#	endif // #ifndef USE_LUA_STUDIO
#endif // #ifdef USE_DEBUGGER
}

void ErrorLog(LPCSTR caMessage)
{
	ai().script_engine().error_log("%s",caMessage);
#ifdef PRINT_CALL_STACK
	ai().script_engine().print_stack();
#endif // #ifdef PRINT_CALL_STACK

#ifdef USE_DEBUGGER
#	ifndef USE_LUA_STUDIO
		if( ai().script_engine().debugger() ){
			ai().script_engine().debugger()->Write(caMessage);
		}
#	endif // #ifndef USE_LUA_STUDIO
#endif // #ifdef USE_DEBUGGER

#ifdef DEBUG
		bool lua_studio_connected = !!ai().script_engine().debugger();
		if (!lua_studio_connected)
#endif //#ifdef DEBUG
	R_ASSERT2(0, caMessage);
}

void FlushLogs()
{
#ifdef DEBUG
	FlushLog();
	ai().script_engine().flush_log();
#endif // DEBUG
}

void verify_if_thread_is_running()
{
	THROW2	(ai().script_engine().current_thread(),"coroutine.yield() is called outside the LUA thread!");
}

bool is_editor()
{
#ifdef XRGAME_EXPORTS
	return		(false);
#else
	return		(true);
#endif
}

#ifdef XRGAME_EXPORTS
CRenderDevice *get_device()
{
	return		(&Device);
}
#endif

int bit_and(int i, int j)
{
	return			(i & j);
}

int bit_or(int i, int j)
{
	return			(i | j);
}

int bit_xor(int i, int j)
{
	return			(i ^ j);
}

int bit_not(int i)
{
	return			(~i);
}

LPCSTR user_name()
{
	return			(Core.UserName);
}

void prefetch_module(LPCSTR file_name)
{
	ai().script_engine().process_file(file_name);
}

struct profile_timer_script {
	u64							m_start_cpu_tick_count;
	u64							m_accumulator;
	u64							m_count;
	int							m_recurse_mark;

	IC								profile_timer_script	()
	{
		m_start_cpu_tick_count	= 0;
		m_accumulator			= 0;
		m_count					= 0;
		m_recurse_mark			= 0;
	}

	IC								profile_timer_script	(const profile_timer_script &profile_timer)
	{
		*this					= profile_timer;
	}

	IC		profile_timer_script&	operator=				(const profile_timer_script &profile_timer)
	{
		m_start_cpu_tick_count	= profile_timer.m_start_cpu_tick_count;
		m_accumulator			= profile_timer.m_accumulator;
		m_count					= profile_timer.m_count;
		m_recurse_mark			= profile_timer.m_recurse_mark;
		return					(*this);
	}

	IC		bool					operator<				(const profile_timer_script &profile_timer) const
	{
		return					(m_accumulator < profile_timer.m_accumulator);
	}

	IC		void					start					()
	{
		if (m_recurse_mark) {
			++m_recurse_mark;
			return;
		}

		++m_recurse_mark;
		++m_count;
		m_start_cpu_tick_count	= CPU::GetCLK();
	}

	IC		void					stop					()
	{
		THROW					(m_recurse_mark);
		--m_recurse_mark;

		if (m_recurse_mark)
			return;

		u64						finish = CPU::GetCLK();
		if (finish > m_start_cpu_tick_count)
			m_accumulator		+= finish - m_start_cpu_tick_count;
	}

	IC		float					time					() const
	{
		FPU::m64r				();
		float					result = (float(double(m_accumulator)/double(CPU::clk_per_second))*1000000.f);
		FPU::m24r				();
		return					(result);
	}
};

IC	profile_timer_script	operator+	(const profile_timer_script &portion0, const profile_timer_script &portion1)
{
	profile_timer_script	result;
	result.m_accumulator	= portion0.m_accumulator + portion1.m_accumulator;
	result.m_count			= portion0.m_count + portion1.m_count;
	return					(result);
}

// IC	std::ostream& operator<<(std::ostream &stream, profile_timer_script &timer)
// {
// 	stream					<< timer.time();
// 	return					(stream);
// }

#ifdef XRGAME_EXPORTS
ICF	u32	script_time_global	()	{ return Device.dwTimeGlobal; }
ICF	u32	script_time_global_async	()	{ return Device.TimerAsync_MMT(); }
#else
ICF	u32	script_time_global	()	{ return 0; }
ICF	u32	script_time_global_async	()	{ return 0; }
#endif

#ifdef XRGAME_EXPORTS
static bool is_enough_address_space_available_impl()
{
	ENGINE_API bool is_enough_address_space_available();
	return is_enough_address_space_available( );
}
#endif // #ifdef XRGAME_EXPORTS

#pragma optimize("s",on)
void CScriptEngine::script_register(lua_State *L)
{
	module(L)[
		class_<profile_timer_script>("profile_timer")
			.def(constructor<>())
			.def(constructor<profile_timer_script&>())
			.def(const_self + profile_timer_script())
			.def(const_self < profile_timer_script())
			.def(tostring(self))
			.def("start",&profile_timer_script::start)
			.def("stop",&profile_timer_script::stop)
			.def("time",&profile_timer_script::time)
	];

	function	(L,	"log",								LuaLog);
	function	(L,	"error_log",						ErrorLog);
	function	(L,	"flush",							FlushLogs);
	function	(L,	"prefetch",							prefetch_module);
	function	(L,	"verify_if_thread_is_running",		verify_if_thread_is_running);
	function	(L,	"editor",							is_editor);
	function	(L,	"bit_and",							bit_and);
	function	(L,	"bit_or",							bit_or);
	function	(L,	"bit_xor",							bit_xor);
	function	(L,	"bit_not",							bit_not);
	function	(L, "user_name",						user_name);
	function	(L, "time_global",						script_time_global);
	function	(L, "time_global_async",				script_time_global_async);
#ifdef XRGAME_EXPORTS
	function	(L,	"device",							get_device);
	function	(L,	"is_enough_address_space_available",is_enough_address_space_available_impl);
#endif // #ifdef XRGAME_EXPORTS
}
<commit_msg>Removed old bit functions<commit_after>////////////////////////////////////////////////////////////////////////////
//	Module 		: script_engine_script.cpp
//	Created 	: 25.12.2002
//  Modified 	: 13.05.2004
//	Author		: Dmitriy Iassenev
//	Description : ALife Simulator script engine export
////////////////////////////////////////////////////////////////////////////

#include "pch_script.h"
#include "script_engine.h"
#include "ai_space.h"
#include "script_debugger.h"

using namespace luabind;

void LuaLog(LPCSTR caMessage)
{
#ifndef MASTER_GOLD
	ai().script_engine().script_log	(ScriptStorage::eLuaMessageTypeMessage,"%s",caMessage);
#endif // #ifndef MASTER_GOLD

#ifdef USE_DEBUGGER
#	ifndef USE_LUA_STUDIO
		if( ai().script_engine().debugger() )
			ai().script_engine().debugger()->Write(caMessage);
#	endif // #ifndef USE_LUA_STUDIO
#endif // #ifdef USE_DEBUGGER
}

void ErrorLog(LPCSTR caMessage)
{
	ai().script_engine().error_log("%s",caMessage);
#ifdef PRINT_CALL_STACK
	ai().script_engine().print_stack();
#endif // #ifdef PRINT_CALL_STACK

#ifdef USE_DEBUGGER
#	ifndef USE_LUA_STUDIO
		if( ai().script_engine().debugger() ){
			ai().script_engine().debugger()->Write(caMessage);
		}
#	endif // #ifndef USE_LUA_STUDIO
#endif // #ifdef USE_DEBUGGER

#ifdef DEBUG
		bool lua_studio_connected = !!ai().script_engine().debugger();
		if (!lua_studio_connected)
#endif //#ifdef DEBUG
	R_ASSERT2(0, caMessage);
}

void FlushLogs()
{
#ifdef DEBUG
	FlushLog();
	ai().script_engine().flush_log();
#endif // DEBUG
}

void verify_if_thread_is_running()
{
	THROW2	(ai().script_engine().current_thread(),"coroutine.yield() is called outside the LUA thread!");
}

bool is_editor()
{
#ifdef XRGAME_EXPORTS
	return		(false);
#else
	return		(true);
#endif
}

#ifdef XRGAME_EXPORTS
CRenderDevice *get_device()
{
	return		(&Device);
}
#endif

LPCSTR user_name()
{
	return			(Core.UserName);
}

void prefetch_module(LPCSTR file_name)
{
	ai().script_engine().process_file(file_name);
}

struct profile_timer_script {
	u64							m_start_cpu_tick_count;
	u64							m_accumulator;
	u64							m_count;
	int							m_recurse_mark;

	IC								profile_timer_script	()
	{
		m_start_cpu_tick_count	= 0;
		m_accumulator			= 0;
		m_count					= 0;
		m_recurse_mark			= 0;
	}

	IC								profile_timer_script	(const profile_timer_script &profile_timer)
	{
		*this					= profile_timer;
	}

	IC		profile_timer_script&	operator=				(const profile_timer_script &profile_timer)
	{
		m_start_cpu_tick_count	= profile_timer.m_start_cpu_tick_count;
		m_accumulator			= profile_timer.m_accumulator;
		m_count					= profile_timer.m_count;
		m_recurse_mark			= profile_timer.m_recurse_mark;
		return					(*this);
	}

	IC		bool					operator<				(const profile_timer_script &profile_timer) const
	{
		return					(m_accumulator < profile_timer.m_accumulator);
	}

	IC		void					start					()
	{
		if (m_recurse_mark) {
			++m_recurse_mark;
			return;
		}

		++m_recurse_mark;
		++m_count;
		m_start_cpu_tick_count	= CPU::GetCLK();
	}

	IC		void					stop					()
	{
		THROW					(m_recurse_mark);
		--m_recurse_mark;

		if (m_recurse_mark)
			return;

		u64						finish = CPU::GetCLK();
		if (finish > m_start_cpu_tick_count)
			m_accumulator		+= finish - m_start_cpu_tick_count;
	}

	IC		float					time					() const
	{
		FPU::m64r				();
		float					result = (float(double(m_accumulator)/double(CPU::clk_per_second))*1000000.f);
		FPU::m24r				();
		return					(result);
	}
};

IC	profile_timer_script	operator+	(const profile_timer_script &portion0, const profile_timer_script &portion1)
{
	profile_timer_script	result;
	result.m_accumulator	= portion0.m_accumulator + portion1.m_accumulator;
	result.m_count			= portion0.m_count + portion1.m_count;
	return					(result);
}

#ifdef XRGAME_EXPORTS
ICF	u32	script_time_global	()	{ return Device.dwTimeGlobal; }
ICF	u32	script_time_global_async	()	{ return Device.TimerAsync_MMT(); }
#else
ICF	u32	script_time_global	()	{ return 0; }
ICF	u32	script_time_global_async	()	{ return 0; }
#endif

#ifdef XRGAME_EXPORTS
static bool is_enough_address_space_available_impl()
{
	ENGINE_API bool is_enough_address_space_available();
	return is_enough_address_space_available( );
}
#endif // #ifdef XRGAME_EXPORTS

#pragma optimize("s",on)
void CScriptEngine::script_register(lua_State *L)
{
	module(L)[
		class_<profile_timer_script>("profile_timer")
			.def(constructor<>())
			.def(constructor<profile_timer_script&>())
			.def(const_self + profile_timer_script())
			.def(const_self < profile_timer_script())
			.def(tostring(self))
			.def("start",&profile_timer_script::start)
			.def("stop",&profile_timer_script::stop)
			.def("time",&profile_timer_script::time)
	];

	function	(L,	"log",								LuaLog);
	function	(L,	"error_log",						ErrorLog);
	function	(L,	"flush",							FlushLogs);
	function	(L,	"prefetch",							prefetch_module);
	function	(L,	"verify_if_thread_is_running",		verify_if_thread_is_running);
	function	(L,	"editor",							is_editor);
	function	(L, "user_name",						user_name);
	function	(L, "time_global",						script_time_global);
	function	(L, "time_global_async",				script_time_global_async);
#ifdef XRGAME_EXPORTS
	function	(L,	"device",							get_device);
	function	(L,	"is_enough_address_space_available",is_enough_address_space_available_impl);
#endif // #ifdef XRGAME_EXPORTS
}
<|endoftext|>


<commit_before>#pragma GCC diagnostic ignored "-Wwrite-strings"

#include "ImageViewPanel.h"

#include "res/btn_actual.xpm"
#include "res/btn_auto.xpm"
#include "res/btn_close.xpm"
#include "res/btn_fit.xpm"
#include "res/btn_next.xpm"
#include "res/btn_pause.xpm"
#include "res/btn_prev.xpm"
#include "res/btn_random.xpm"
#include "res/btn_save.xpm"
#include "res/img_picture.xpm"
#include <algorithm>
#include <cmath>
#include <ctime>
#include <wx/mstream.h>
#include <wx/notebook.h>
#include <wx/statline.h>
#include <wx/wfstream.h>

#ifdef USE_CIMG
using cimg_library::CImg;
#endif

ImageViewPanel::ImageViewPanel(wxWindow *parent, Entry *entry, wxWindowID id,
                               const wxPoint &pos, const wxSize &size,
                               long style, const wxString &name)
    : wxPanel(parent, id, pos, size, style, name), timer(this),
      randomEngine(r()), image(IMG_PICTURE_xpm) {
  auto outerSizer = new wxBoxSizer(wxVERTICAL);
  auto btnSizer = new wxBoxSizer(wxHORIZONTAL);
  btnClose = new wxButton(this, wxID_CLOSE, "Close", wxDefaultPosition,
                          wxDefaultSize, wxBU_EXACTFIT | wxBU_NOTEXT);
  btnClose->SetBitmap(wxICON(IDI_ICON_CLOSE));
  btnClose->SetToolTip("Close this tab.");
  btnClose->Bind(wxEVT_BUTTON, &ImageViewPanel::OnCloseButtonClick, this);

  spnScale = new wxSpinCtrl(this, wxID_ANY);
  spnScale->SetToolTip("Set the percentage the image is scaled to.");
  spnScale->SetValue(100);
  spnScale->SetRange(1, 200);
  spnScale->Bind(wxEVT_SPINCTRL, &ImageViewPanel::OnScaleValueChanged, this);

  btnFitSize = new wxToggleButton(this, wxID_ANY, "Fit", wxDefaultPosition,
                                  wxDefaultSize, wxBU_EXACTFIT | wxBU_NOTEXT);
  btnFitSize->SetBitmap(wxICON(IDI_ICON_FIT));
  btnFitSize->SetToolTip("Fit the image inside the tab.");

  btnFitSize->Bind(wxEVT_TOGGLEBUTTON, &ImageViewPanel::OnBtnFitSizeToggle,
                   this);
  btnFitSize->SetValue(true);

  btnActualSize = new wxButton(this, wxID_ANY, "Actual", wxDefaultPosition,
                               wxDefaultSize, wxBU_EXACTFIT | wxBU_NOTEXT);
  btnActualSize->Bind(wxEVT_BUTTON, &ImageViewPanel::OnBtnActualSizePressed,
                      this);
  btnActualSize->SetBitmap(wxICON(IDI_ICON_ACTUAL));
  btnActualSize->SetToolTip("Scale the image back to the original size.");
  btnActualSize->Disable();
  spnScale->Disable();
  btnSizer->Add(btnClose, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL);
  btnSizer->AddSpacer(5);
  btnSizer->Add(new wxStaticLine(this, wxID_ANY, wxDefaultPosition,
                                 wxDefaultSize, wxLI_VERTICAL),
                0, wxRIGHT | wxALIGN_CENTER_VERTICAL | wxEXPAND, 5);
  btnSizer->Add(spnScale, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5);
  btnSizer->Add(new wxStaticText(this, wxID_ANY, "%", wxDefaultPosition,
                                 wxDefaultSize, wxST_NO_AUTORESIZE),
                wxRIGHT | wxALIGN_CENTER_VERTICAL, 5);
  btnSizer->Add(btnFitSize, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5);
  btnSizer->Add(btnActualSize, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5);
  btnSizer->Add(new wxStaticLine(this, wxID_ANY, wxDefaultPosition,
                                 wxDefaultSize, wxLI_VERTICAL),
                0, wxRIGHT | wxALIGN_CENTER_VERTICAL | wxEXPAND, 5);

  btnNext = new wxButton(this, wxID_ANY, "Next", wxDefaultPosition,
                         wxDefaultSize, wxBU_EXACTFIT | wxBU_NOTEXT);
  btnNext->Bind(wxEVT_BUTTON, [this](wxCommandEvent &event) { Advance(1); },
                wxID_ANY);
  btnNext->SetBitmap(wxICON(IDI_ICON_NEXT));
  btnNext->SetToolTip("Load the next image from the same directory.");
  btnPrev = new wxButton(this, wxID_ANY, "Prev", wxDefaultPosition,
                         wxDefaultSize, wxBU_EXACTFIT | wxBU_NOTEXT);
  btnPrev->Bind(wxEVT_BUTTON, &ImageViewPanel::OnPrevButtonClick, this);
  btnPrev->SetBitmap(wxICON(IDI_ICON_PREV));
  btnPrev->SetToolTip("Load the previous image from the same directory.");

  btnAuto = new wxToggleButton(this, wxID_ANY, "Auto", wxDefaultPosition,
                               wxDefaultSize, wxBU_EXACTFIT | wxBU_NOTEXT);
  btnAuto->Bind(wxEVT_TOGGLEBUTTON, &ImageViewPanel::OnBtnAutoToggle, this);
  btnAuto->SetBitmap(wxICON(IDI_ICON_AUTO));
  btnAuto->SetBitmapPressed(wxICON(IDI_ICON_PAUSE));
  btnAuto->SetToolTip("Auto advance to the next image");

  btnRandom = new wxToggleButton(this, wxID_ANY, "Auto", wxDefaultPosition,
                                 wxDefaultSize, wxBU_EXACTFIT | wxBU_NOTEXT);
  btnRandom->Bind(wxEVT_TOGGLEBUTTON, &ImageViewPanel::OnBtnAutoToggle, this);
  btnRandom->SetBitmap(wxICON(IDI_ICON_RANDOM));
  btnRandom->SetToolTip("Advance image randomly");

  spnRefreshTime = new wxSpinCtrl(this, wxID_ANY);
  spnRefreshTime->SetValue(30);
  spnRefreshTime->SetRange(1, 3000);
  spnRefreshTime->SetToolTip("Set the time interval (seconds) between the "
                             "current image to the next one.");

  btnSizer->Add(btnPrev, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5);
  btnSizer->Add(btnNext, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5);
  btnSizer->Add(btnAuto, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5);
  btnSizer->Add(spnRefreshTime, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL);
  btnSizer->Add(new wxStaticText(this, wxID_ANY, "s", wxDefaultPosition,
                                 wxDefaultSize, wxST_NO_AUTORESIZE),
                0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5);
  btnSizer->Add(btnRandom, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5);

  auto btnSave = new wxButton(this, wxID_ANY, "Save As", wxDefaultPosition,
                              wxDefaultSize, wxBU_EXACTFIT | wxBU_NOTEXT);
  btnSave->SetBitmap(wxICON(IDI_ICON_SAVE));
  btnSave->SetToolTip("Save file as");
  btnSave->Bind(wxEVT_BUTTON, &ImageViewPanel::OnSaveButtonClick, this);

  btnSizer->Add(new wxStaticLine(this, wxID_ANY, wxDefaultPosition,
                                 wxDefaultSize, wxLI_VERTICAL),
                0, wxRIGHT | wxALIGN_CENTER_VERTICAL | wxEXPAND, 5);
  btnSizer->Add(btnSave, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5);

  outerSizer->Add(btnSizer, 0, wxALL, 10);
  outerSizer->Add(new wxStaticLine(this), 0,
                  wxRIGHT | wxALIGN_CENTER_VERTICAL | wxEXPAND, 10);
  scrollPanel = new wxScrolledWindow(this, wxID_ANY);
  scrollSizer = new wxBoxSizer(wxVERTICAL);

  scrollPanel->SetSizerAndFit(scrollSizer);
  scrollPanel->SetScrollRate(10, 10);

  outerSizer->Add(scrollPanel, 1, wxEXPAND | wxALL, 10);
  SetSizer(outerSizer);

  auto entryParent = entry->Parent();
  for (int i = 0; i < entryParent->Count(); ++i) {
    Entry *childEntry = (*entryParent)[i];

    if (childEntry->IsDirectory())
      continue;
    auto ext = childEntry->Name().AfterLast('.').Lower();

    if (ext != "jpg" && ext != "jpeg" && ext != "png" && ext != "gif")
      continue;

    entries.push_back(childEntry);
  }

  entryIter = std::find(entries.begin(), entries.end(), entry);

  bitmapStatic = new wxStaticBitmap(scrollPanel, wxID_ANY, wxBitmap(image));
  scrollSizer->Add(bitmapStatic);

  Bind(wxEVT_TIMER, &ImageViewPanel::OnTimerNotify, this);

  if (entries.size() > 0)
    random = std::uniform_int_distribution<int>(1, entries.size());
  else
    random = std::uniform_int_distribution<int>(0, 1);
}

void ImageViewPanel::OnCloseButtonClick(wxCommandEvent &event) {
  auto notebook = dynamic_cast<wxNotebook *>(GetParent());
  if (!notebook)
    return;
  auto currentPage = notebook->GetSelection();
  if (currentPage == wxNOT_FOUND)
    return;
  notebook->DeletePage(currentPage);
}

void ImageViewPanel::OnScaleValueChanged(wxSpinEvent &event) {
  RefreshImage(event.GetPosition());
}

void ImageViewPanel::OnBtnActualSizePressed(wxCommandEvent &event) {
  spnScale->SetValue(100);
  RefreshImage();
}

void ImageViewPanel::OnBtnFitSizeToggle(wxCommandEvent &event) {
  if (btnFitSize->GetValue()) {
    FitImage();
    btnActualSize->Disable();
    spnScale->Disable();
  } else {
    btnActualSize->Enable();
    spnScale->Enable();
  }
}

void ImageViewPanel::FitImage() {
  auto ctrlSize = scrollPanel->GetSize();
  auto imageSize = image.GetSize();

  auto widthPercent = (ctrlSize.GetWidth() * 100) / imageSize.GetWidth();
  auto heightPercent = (ctrlSize.GetHeight() * 100) / imageSize.GetHeight();

  auto &percentage =
      widthPercent < heightPercent ? widthPercent : heightPercent;
  percentage = percentage > 200 ? 200 : percentage;
  spnScale->SetValue(percentage);
  RefreshImage(percentage);
}

void ImageViewPanel::RefreshImage(const int &percentage) {
  if (percentage == 100) {
    bitmapStatic->SetBitmap(wxBitmap(image));
  } else {
    int width = (image.GetWidth() * percentage) / 100;
    int height = (image.GetHeight() * percentage) / 100;

    bitmapStatic->SetBitmap(wxBitmap(CreateScaledImage(width, height)));
  }
  scrollSizer->FitInside(scrollPanel);
  spnScale->SetValue(percentage);
  Refresh();
  Update();
}

void ImageViewPanel::OnNextButtonClick(wxCommandEvent &event) { Advance(1); }

void ImageViewPanel::OnPrevButtonClick(wxCommandEvent &event) { Advance(-1); }

void ImageViewPanel::SetImageEntry(Entry *entry) {
  auto notebook = dynamic_cast<wxNotebook *>(GetParent());
  if (!notebook)
    return;
  auto currentPage = notebook->GetSelection();
  if (currentPage == wxNOT_FOUND)
    return;
  notebook->SetPageText(currentPage, entry->Name());

  image = entry->CreateImage();
#ifdef USE_CIMG
  cimgImage = CImgFromWxImage(image);
  if (image.HasAlpha()) {
    cimgAlpha = CImgAlphaFromWxImage(image);
  }
#endif
  if (btnFitSize->GetValue())
    FitImage();
  else
    RefreshImage();
}

bool ImageViewPanel::Layout() {
  wxPanel::Layout();
  if (btnFitSize->GetValue())
    FitImage();
  return false;
}

void ImageViewPanel::Advance(const int &step) {
  if (step == 0)
    return;
  bool isForward = step >= 0;
  for (int i = 0; i < std::abs(step); i++) {
    if (isForward) {
      entryIter++;
      if (entryIter == entries.end())
        entryIter = entries.begin();
    } else {
      entryIter--;
      if (entryIter < entries.begin())
        entryIter = entries.end() - 1;
    }
  }

  SetImageEntry(*entryIter);
}

void ImageViewPanel::OnBtnAutoToggle(wxCommandEvent &event) {
  if (btnAuto->GetValue()) {
    timer.Start(spnRefreshTime->GetValue() * 1000);
    btnRandom->Disable();
    spnRefreshTime->Disable();
    btnPrev->Disable();
    btnNext->Disable();
  } else {
    timer.Stop();
    btnRandom->Enable();
    spnRefreshTime->Enable();
    btnPrev->Enable();
    btnNext->Enable();
  }
}

void ImageViewPanel::OnTimerNotify(wxTimerEvent &timer) {
  Advance(btnRandom->GetValue() ? random(randomEngine) : 1);
}

void ImageViewPanel::OnSaveButtonClick(wxCommandEvent &event) {
  Entry *entry = *entryIter;
  wxFileDialog dialog(this, "Save As...", filename.GetPath(), entry->Name(),
                      wxFileSelectorDefaultWildcardStr, wxFD_SAVE);
  if (dialog.ShowModal() != wxID_OK)
    return;

  filename = dialog.GetPath();
  wxFileOutputStream output(dialog.GetPath());
  entry->WriteStream(output);
  output.Close();
}

void ImageViewPanel::SwitchToTheActualImage() { SetImageEntry(*entryIter); }

wxImage ImageViewPanel::CreateScaledImage(const unsigned int &width,
                                          const unsigned int &height,
                                          const bool isHighQuality) {
  if (width == 0 || height == 0)
    return wxImage(1, 1);

  if (!isHighQuality)
    return image.Scale(width, height);
#ifdef USE_CIMG
  constexpr auto quality = 6;
  auto scaled =
      cimgImage.get_resize(width, height, 1, cimgImage.spectrum(), quality);

  if (image.HasAlpha()) {
    auto scaledAlpha =
        cimgAlpha.get_resize(width, height, 1, cimgAlpha.spectrum(), quality);
    return wxImageFromCImg(scaled, &scaledAlpha);
  } else {
    return wxImageFromCImg(scaled);
  }
#else
  return image.Scale(width, height, wxIMAGE_QUALITY_HIGH);
#endif
}

#ifdef USE_CIMG
/* FIXME:
Currently CImg is used for image processing. CImg expects deinterleaved image
data which is different than what wxImage expects. This results in having to
juggle arround the data buffer, dimension value, etc. to make things work.
Certainly it causes performance impacts.
CImg should be replaced with other code that process interleaved image
buffer.

This option is disabled by default due to performance problems.
*/

CImg<unsigned char> ImageViewPanel::CImgFromWxImage(const wxImage &image) {
  CImg<unsigned char> output(image.GetData(), 3, image.GetWidth(),
                             image.GetHeight(), 1);
  output.permute_axes("yzcx");
  return output;
}

CImg<unsigned char> ImageViewPanel::CImgAlphaFromWxImage(const wxImage &image) {
  if (!image.HasAlpha())
    return CImg<unsigned char>();

  return CImg<unsigned char>(image.GetAlpha(), image.GetWidth(),
                             image.GetHeight(), 1, 1);
}

wxImage ImageViewPanel::wxImageFromCImg(const CImg<unsigned char> &img,
                                        CImg<unsigned char> *alpha) {
  auto width = img.width();
  auto height = img.height();
  auto dataSize = width * height * 3;

  CImg<unsigned char> rearranged = img.get_permute_axes("cxyz");

  auto data = (unsigned char *)malloc(dataSize);
  memcpy(data, rearranged.data(), dataSize);

  wxImage output(width, height);
  output.SetData(data);

  if (alpha != nullptr) {
    auto alphaSize = width * height;

    auto alphaData = (unsigned char *)malloc(alphaSize);
    memcpy(alphaData, alpha->data(), alphaSize);

    output.SetAlpha(alphaData);
  }
  return output;
}
#endif
<commit_msg>Fix crashes when enable CIMG option<commit_after>#pragma GCC diagnostic ignored "-Wwrite-strings"

#include "ImageViewPanel.h"

#include "res/btn_actual.xpm"
#include "res/btn_auto.xpm"
#include "res/btn_close.xpm"
#include "res/btn_fit.xpm"
#include "res/btn_next.xpm"
#include "res/btn_pause.xpm"
#include "res/btn_prev.xpm"
#include "res/btn_random.xpm"
#include "res/btn_save.xpm"
#include "res/img_picture.xpm"
#include <algorithm>
#include <cmath>
#include <ctime>
#include <wx/mstream.h>
#include <wx/notebook.h>
#include <wx/statline.h>
#include <wx/wfstream.h>

#ifdef USE_CIMG
using cimg_library::CImg;
#endif

ImageViewPanel::ImageViewPanel(wxWindow *parent, Entry *entry, wxWindowID id,
                               const wxPoint &pos, const wxSize &size,
                               long style, const wxString &name)
    : wxPanel(parent, id, pos, size, style, name), timer(this),
      randomEngine(r()), image(IMG_PICTURE_xpm) {
  auto outerSizer = new wxBoxSizer(wxVERTICAL);
  auto btnSizer = new wxBoxSizer(wxHORIZONTAL);
  btnClose = new wxButton(this, wxID_CLOSE, "Close", wxDefaultPosition,
                          wxDefaultSize, wxBU_EXACTFIT | wxBU_NOTEXT);
  btnClose->SetBitmap(wxICON(IDI_ICON_CLOSE));
  btnClose->SetToolTip("Close this tab.");
  btnClose->Bind(wxEVT_BUTTON, &ImageViewPanel::OnCloseButtonClick, this);

  spnScale = new wxSpinCtrl(this, wxID_ANY);
  spnScale->SetToolTip("Set the percentage the image is scaled to.");
  spnScale->SetValue(100);
  spnScale->SetRange(1, 200);
  spnScale->Bind(wxEVT_SPINCTRL, &ImageViewPanel::OnScaleValueChanged, this);

  btnFitSize = new wxToggleButton(this, wxID_ANY, "Fit", wxDefaultPosition,
                                  wxDefaultSize, wxBU_EXACTFIT | wxBU_NOTEXT);
  btnFitSize->SetBitmap(wxICON(IDI_ICON_FIT));
  btnFitSize->SetToolTip("Fit the image inside the tab.");

  btnFitSize->Bind(wxEVT_TOGGLEBUTTON, &ImageViewPanel::OnBtnFitSizeToggle,
                   this);
  btnFitSize->SetValue(true);

  btnActualSize = new wxButton(this, wxID_ANY, "Actual", wxDefaultPosition,
                               wxDefaultSize, wxBU_EXACTFIT | wxBU_NOTEXT);
  btnActualSize->Bind(wxEVT_BUTTON, &ImageViewPanel::OnBtnActualSizePressed,
                      this);
  btnActualSize->SetBitmap(wxICON(IDI_ICON_ACTUAL));
  btnActualSize->SetToolTip("Scale the image back to the original size.");
  btnActualSize->Disable();
  spnScale->Disable();
  btnSizer->Add(btnClose, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL);
  btnSizer->AddSpacer(5);
  btnSizer->Add(new wxStaticLine(this, wxID_ANY, wxDefaultPosition,
                                 wxDefaultSize, wxLI_VERTICAL),
                0, wxRIGHT | wxALIGN_CENTER_VERTICAL | wxEXPAND, 5);
  btnSizer->Add(spnScale, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5);
  btnSizer->Add(new wxStaticText(this, wxID_ANY, "%", wxDefaultPosition,
                                 wxDefaultSize, wxST_NO_AUTORESIZE),
                wxRIGHT | wxALIGN_CENTER_VERTICAL, 5);
  btnSizer->Add(btnFitSize, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5);
  btnSizer->Add(btnActualSize, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5);
  btnSizer->Add(new wxStaticLine(this, wxID_ANY, wxDefaultPosition,
                                 wxDefaultSize, wxLI_VERTICAL),
                0, wxRIGHT | wxALIGN_CENTER_VERTICAL | wxEXPAND, 5);

  btnNext = new wxButton(this, wxID_ANY, "Next", wxDefaultPosition,
                         wxDefaultSize, wxBU_EXACTFIT | wxBU_NOTEXT);
  btnNext->Bind(wxEVT_BUTTON, [this](wxCommandEvent &event) { Advance(1); },
                wxID_ANY);
  btnNext->SetBitmap(wxICON(IDI_ICON_NEXT));
  btnNext->SetToolTip("Load the next image from the same directory.");
  btnPrev = new wxButton(this, wxID_ANY, "Prev", wxDefaultPosition,
                         wxDefaultSize, wxBU_EXACTFIT | wxBU_NOTEXT);
  btnPrev->Bind(wxEVT_BUTTON, &ImageViewPanel::OnPrevButtonClick, this);
  btnPrev->SetBitmap(wxICON(IDI_ICON_PREV));
  btnPrev->SetToolTip("Load the previous image from the same directory.");

  btnAuto = new wxToggleButton(this, wxID_ANY, "Auto", wxDefaultPosition,
                               wxDefaultSize, wxBU_EXACTFIT | wxBU_NOTEXT);
  btnAuto->Bind(wxEVT_TOGGLEBUTTON, &ImageViewPanel::OnBtnAutoToggle, this);
  btnAuto->SetBitmap(wxICON(IDI_ICON_AUTO));
  btnAuto->SetBitmapPressed(wxICON(IDI_ICON_PAUSE));
  btnAuto->SetToolTip("Auto advance to the next image");

  btnRandom = new wxToggleButton(this, wxID_ANY, "Auto", wxDefaultPosition,
                                 wxDefaultSize, wxBU_EXACTFIT | wxBU_NOTEXT);
  btnRandom->Bind(wxEVT_TOGGLEBUTTON, &ImageViewPanel::OnBtnAutoToggle, this);
  btnRandom->SetBitmap(wxICON(IDI_ICON_RANDOM));
  btnRandom->SetToolTip("Advance image randomly");

  spnRefreshTime = new wxSpinCtrl(this, wxID_ANY);
  spnRefreshTime->SetValue(30);
  spnRefreshTime->SetRange(1, 3000);
  spnRefreshTime->SetToolTip("Set the time interval (seconds) between the "
                             "current image to the next one.");

  btnSizer->Add(btnPrev, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5);
  btnSizer->Add(btnNext, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5);
  btnSizer->Add(btnAuto, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5);
  btnSizer->Add(spnRefreshTime, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL);
  btnSizer->Add(new wxStaticText(this, wxID_ANY, "s", wxDefaultPosition,
                                 wxDefaultSize, wxST_NO_AUTORESIZE),
                0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5);
  btnSizer->Add(btnRandom, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5);

  auto btnSave = new wxButton(this, wxID_ANY, "Save As", wxDefaultPosition,
                              wxDefaultSize, wxBU_EXACTFIT | wxBU_NOTEXT);
  btnSave->SetBitmap(wxICON(IDI_ICON_SAVE));
  btnSave->SetToolTip("Save file as");
  btnSave->Bind(wxEVT_BUTTON, &ImageViewPanel::OnSaveButtonClick, this);

  btnSizer->Add(new wxStaticLine(this, wxID_ANY, wxDefaultPosition,
                                 wxDefaultSize, wxLI_VERTICAL),
                0, wxRIGHT | wxALIGN_CENTER_VERTICAL | wxEXPAND, 5);
  btnSizer->Add(btnSave, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5);

  outerSizer->Add(btnSizer, 0, wxALL, 10);
  outerSizer->Add(new wxStaticLine(this), 0,
                  wxRIGHT | wxALIGN_CENTER_VERTICAL | wxEXPAND, 10);
  scrollPanel = new wxScrolledWindow(this, wxID_ANY);
  scrollSizer = new wxBoxSizer(wxVERTICAL);

  scrollPanel->SetSizerAndFit(scrollSizer);
  scrollPanel->SetScrollRate(10, 10);

  outerSizer->Add(scrollPanel, 1, wxEXPAND | wxALL, 10);
  SetSizer(outerSizer);

  auto entryParent = entry->Parent();
  for (int i = 0; i < entryParent->Count(); ++i) {
    Entry *childEntry = (*entryParent)[i];

    if (childEntry->IsDirectory())
      continue;
    auto ext = childEntry->Name().AfterLast('.').Lower();

    if (ext != "jpg" && ext != "jpeg" && ext != "png" && ext != "gif")
      continue;

    entries.push_back(childEntry);
  }

  entryIter = std::find(entries.begin(), entries.end(), entry);

  bitmapStatic = new wxStaticBitmap(scrollPanel, wxID_ANY, wxBitmap(image));
  scrollSizer->Add(bitmapStatic);

  Bind(wxEVT_TIMER, &ImageViewPanel::OnTimerNotify, this);

  if (entries.size() > 0)
    random = std::uniform_int_distribution<int>(1, entries.size());
  else
    random = std::uniform_int_distribution<int>(0, 1);

  #ifdef USE_CIMG
    cimgImage = CImgFromWxImage(image);
  #endif
}

void ImageViewPanel::OnCloseButtonClick(wxCommandEvent &event) {
  auto notebook = dynamic_cast<wxNotebook *>(GetParent());
  if (!notebook)
    return;
  auto currentPage = notebook->GetSelection();
  if (currentPage == wxNOT_FOUND)
    return;
  notebook->DeletePage(currentPage);
}

void ImageViewPanel::OnScaleValueChanged(wxSpinEvent &event) {
  RefreshImage(event.GetPosition());
}

void ImageViewPanel::OnBtnActualSizePressed(wxCommandEvent &event) {
  spnScale->SetValue(100);
  RefreshImage();
}

void ImageViewPanel::OnBtnFitSizeToggle(wxCommandEvent &event) {
  if (btnFitSize->GetValue()) {
    FitImage();
    btnActualSize->Disable();
    spnScale->Disable();
  } else {
    btnActualSize->Enable();
    spnScale->Enable();
  }
}

void ImageViewPanel::FitImage() {
  auto ctrlSize = scrollPanel->GetSize();
  auto imageSize = image.GetSize();

  auto widthPercent = (ctrlSize.GetWidth() * 100) / imageSize.GetWidth();
  auto heightPercent = (ctrlSize.GetHeight() * 100) / imageSize.GetHeight();

  auto &percentage =
      widthPercent < heightPercent ? widthPercent : heightPercent;
  percentage = percentage > 200 ? 200 : percentage;
  spnScale->SetValue(percentage);
  RefreshImage(percentage);
}

void ImageViewPanel::RefreshImage(const int &percentage) {
  if (percentage == 100) {
    bitmapStatic->SetBitmap(wxBitmap(image));
  } else {
    int width = (image.GetWidth() * percentage) / 100;
    int height = (image.GetHeight() * percentage) / 100;

    bitmapStatic->SetBitmap(wxBitmap(CreateScaledImage(width, height, true)));
  }
  scrollSizer->FitInside(scrollPanel);
  spnScale->SetValue(percentage);
  Refresh();
  Update();
}

void ImageViewPanel::OnNextButtonClick(wxCommandEvent &event) { Advance(1); }

void ImageViewPanel::OnPrevButtonClick(wxCommandEvent &event) { Advance(-1); }

void ImageViewPanel::SetImageEntry(Entry *entry) {
  auto notebook = dynamic_cast<wxNotebook *>(GetParent());
  if (!notebook)
    return;
  auto currentPage = notebook->GetSelection();
  if (currentPage == wxNOT_FOUND)
    return;
  notebook->SetPageText(currentPage, entry->Name());

  image = entry->CreateImage();
#ifdef USE_CIMG
  cimgImage = CImgFromWxImage(image);
  if (image.HasAlpha()) {
    cimgAlpha = CImgAlphaFromWxImage(image);
  }
#endif
  if (btnFitSize->GetValue())
    FitImage();
  else
    RefreshImage();
}

bool ImageViewPanel::Layout() {
  wxPanel::Layout();
  if (btnFitSize->GetValue())
    FitImage();
  return false;
}

void ImageViewPanel::Advance(const int &step) {
  if (step == 0)
    return;
  bool isForward = step >= 0;
  for (int i = 0; i < std::abs(step); i++) {
    if (isForward) {
      entryIter++;
      if (entryIter == entries.end())
        entryIter = entries.begin();
    } else {
      entryIter--;
      if (entryIter < entries.begin())
        entryIter = entries.end() - 1;
    }
  }

  SetImageEntry(*entryIter);
}

void ImageViewPanel::OnBtnAutoToggle(wxCommandEvent &event) {
  if (btnAuto->GetValue()) {
    timer.Start(spnRefreshTime->GetValue() * 1000);
    btnRandom->Disable();
    spnRefreshTime->Disable();
    btnPrev->Disable();
    btnNext->Disable();
  } else {
    timer.Stop();
    btnRandom->Enable();
    spnRefreshTime->Enable();
    btnPrev->Enable();
    btnNext->Enable();
  }
}

void ImageViewPanel::OnTimerNotify(wxTimerEvent &timer) {
  Advance(btnRandom->GetValue() ? random(randomEngine) : 1);
}

void ImageViewPanel::OnSaveButtonClick(wxCommandEvent &event) {
  Entry *entry = *entryIter;
  wxFileDialog dialog(this, "Save As...", filename.GetPath(), entry->Name(),
                      wxFileSelectorDefaultWildcardStr, wxFD_SAVE);
  if (dialog.ShowModal() != wxID_OK)
    return;

  filename = dialog.GetPath();
  wxFileOutputStream output(dialog.GetPath());
  entry->WriteStream(output);
  output.Close();
}

void ImageViewPanel::SwitchToTheActualImage() { SetImageEntry(*entryIter); }

wxImage ImageViewPanel::CreateScaledImage(const unsigned int &width,
                                          const unsigned int &height,
                                          const bool isHighQuality) {
  if (width == 0 || height == 0)
    return wxImage(1, 1);

  if (!isHighQuality)
    return image.Scale(width, height);
#ifdef USE_CIMG
  constexpr auto quality = 6;
  auto scaled =
      cimgImage.get_resize(width, height, 1, cimgImage.spectrum(), quality);

  if (image.HasAlpha()) {
    auto scaledAlpha =
        cimgAlpha.get_resize(width, height, 1, cimgAlpha.spectrum(), quality);
    return wxImageFromCImg(scaled, &scaledAlpha);
  } else {
    return wxImageFromCImg(scaled);
  }
#else
  return image.Scale(width, height, wxIMAGE_QUALITY_HIGH);
#endif
}

#ifdef USE_CIMG
/* FIXME:
Currently CImg is used for image processing. CImg expects deinterleaved image
data which is different than what wxImage expects. This results in having to
juggle arround the data buffer, dimension value, etc. to make things work.
Certainly it causes performance impacts.
CImg should be replaced with other code that process interleaved image
buffer.

This option is disabled by default due to performance problems.
*/

CImg<unsigned char> ImageViewPanel::CImgFromWxImage(const wxImage &image) {
  CImg<unsigned char> output(image.GetData(), 3, image.GetWidth(),
                             image.GetHeight(), 1);
  output.permute_axes("yzcx");
  return output;
}

CImg<unsigned char> ImageViewPanel::CImgAlphaFromWxImage(const wxImage &image) {
  if (!image.HasAlpha())
    return CImg<unsigned char>();

  return CImg<unsigned char>(image.GetAlpha(), image.GetWidth(),
                             image.GetHeight(), 1, 1);
}

wxImage ImageViewPanel::wxImageFromCImg(const CImg<unsigned char> &img,
                                        CImg<unsigned char> *alpha) {
  auto width = img.width();
  auto height = img.height();
  auto dataSize = width * height * 3;

  CImg<unsigned char> rearranged = img.get_permute_axes("cxyz");

  auto data = (unsigned char *)malloc(dataSize);
  memcpy(data, rearranged.data(), dataSize);

  wxImage output(width, height);
  output.SetData(data);

  if (alpha != nullptr) {
    auto alphaSize = width * height;

    auto alphaData = (unsigned char *)malloc(alphaSize);
    memcpy(alphaData, alpha->data(), alphaSize);

    output.SetAlpha(alphaData);
  }
  return output;
}
#endif
<|endoftext|>


<commit_before>/**
 *  @copyright Copyright 2017 The J-PET Framework Authors. All rights reserved.
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may find a copy of the License in the LICENCE file.
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *  @file main.cpp
 */

#include "JPetManager/JPetManager.h"
#include "ImageReco.h"
#include "SinogramCreator.h"
#include "SinogramCreatorMC.h"
using namespace std;

int main(int argc, const char* argv[])
{
  JPetManager& manager = JPetManager::getManager();

  manager.registerTask<ImageReco>("ImageReco");
  manager.registerTask<SinogramCreator>("SinogramCreator");
  manager.registerTask<SinogramCreatorMC>("SinogramCreatorMC");

  //manager.useTask("ImageReco", "unk.evt", "reco");
  //manager.useTask("SinogramCreator", "unk.evt", "sino");
  manager.useTask("SinogramCreatorMC", "unk.evt", "sino.mc");

  manager.run(argc, argv);
}
<commit_msg>Remove SinogramCreatorMC<commit_after>/**
 *  @copyright Copyright 2017 The J-PET Framework Authors. All rights reserved.
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may find a copy of the License in the LICENCE file.
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *  @file main.cpp
 */

#include "JPetManager/JPetManager.h"
#include "ImageReco.h"
#include "SinogramCreator.h"
using namespace std;

int main(int argc, const char *argv[])
{
  JPetManager &manager = JPetManager::getManager();

  manager.registerTask<ImageReco>("ImageReco");
  manager.registerTask<SinogramCreator>("SinogramCreator");

  //manager.useTask("ImageReco", "unk.evt", "reco");
  manager.useTask("SinogramCreator", "unk.evt", "sino");

  manager.run(argc, argv);
}
<|endoftext|>


<commit_before>#include "callback_parameter.hpp"
#include "callback_object.hpp"
#include "code/ylikuutio/common/any_value.hpp"
#include "code/ylikuutio/hierarchy/hierarchy_templates.hpp"

// Include standard headers
#include <iostream> // std::cout, std::cin, std::cerr
#include <string>   // std::string

namespace callback_system
{
    CallbackParameter::CallbackParameter(std::string name, datatypes::AnyValue* any_value, bool is_reference, callback_system::CallbackObject* parent_pointer)
    {
        // constructor.
        this->name = name;
        this->any_value = any_value;
        this->is_reference = is_reference;
        this->parent_pointer = parent_pointer;

        // get childID from the CallbackObject and set pointer to this CallbackParameter.
        this->bind_to_parent();
    }

    CallbackParameter::~CallbackParameter()
    {
        // destructor.
        std::cout << "Callback parameter with childID " << this->childID << " will be destroyed.\n";

        // destroy AnyValue.
        delete this->any_value;

        // set pointer to this object to nullptr.
        this->parent_pointer->set_callback_parameter_pointer(this->childID, nullptr);
    }

    datatypes::AnyValue* CallbackParameter::get_any_value()
    {
        return this->any_value;
    }

    void CallbackParameter::bind_to_parent()
    {
        hierarchy::bind_child_to_parent<callback_system::CallbackParameter*>(this, this->parent_pointer->callback_parameter_pointer_vector, this->parent_pointer->free_callback_parameterID_queue);

        if (!this->name.empty())
        {
            // This is a named variable, so store it in parent's `anyvalue_hashmap`.
            this->parent_pointer->set_any_value(this->name, this->any_value);
        }
    }
}
<commit_msg>Moved function `void CallbackParameter::bind_to_parent()` up.<commit_after>#include "callback_parameter.hpp"
#include "callback_object.hpp"
#include "code/ylikuutio/common/any_value.hpp"
#include "code/ylikuutio/hierarchy/hierarchy_templates.hpp"

// Include standard headers
#include <iostream> // std::cout, std::cin, std::cerr
#include <string>   // std::string

namespace callback_system
{
    void CallbackParameter::bind_to_parent()
    {
        hierarchy::bind_child_to_parent<callback_system::CallbackParameter*>(this, this->parent_pointer->callback_parameter_pointer_vector, this->parent_pointer->free_callback_parameterID_queue);

        if (!this->name.empty())
        {
            // This is a named variable, so store it in parent's `anyvalue_hashmap`.
            this->parent_pointer->set_any_value(this->name, this->any_value);
        }
    }

    CallbackParameter::CallbackParameter(std::string name, datatypes::AnyValue* any_value, bool is_reference, callback_system::CallbackObject* parent_pointer)
    {
        // constructor.
        this->name = name;
        this->any_value = any_value;
        this->is_reference = is_reference;
        this->parent_pointer = parent_pointer;

        // get childID from the CallbackObject and set pointer to this CallbackParameter.
        this->bind_to_parent();
    }

    CallbackParameter::~CallbackParameter()
    {
        // destructor.
        std::cout << "Callback parameter with childID " << this->childID << " will be destroyed.\n";

        // destroy AnyValue.
        delete this->any_value;

        // set pointer to this object to nullptr.
        this->parent_pointer->set_callback_parameter_pointer(this->childID, nullptr);
    }

    datatypes::AnyValue* CallbackParameter::get_any_value()
    {
        return this->any_value;
    }
}
<|endoftext|>