#include "Riostream.h"
#include "TAxis.h"
#include "TVirtualPad.h"
#include "TStyle.h"
#include "TError.h"
#include "THashList.h"
#include "TH1.h"
#include "TObjString.h"
#include "TDatime.h"
#include "TTimeStamp.h"
#include "TROOT.h"
#include "TClass.h"
#include "TMath.h"
#include <time.h>
ClassImp(TAxis)
TAxis::TAxis(): TNamed(), TAttAxis()
{
fNbins = 1;
fXmin = 0;
fXmax = 1;
fFirst = 0;
fLast = 0;
fParent = 0;
fLabels = 0;
fBits2 = 0;
fTimeDisplay = 0;
}
TAxis::TAxis(Int_t nbins,Double_t xlow,Double_t xup): TNamed(), TAttAxis()
{
fParent = 0;
fLabels = 0;
Set(nbins,xlow,xup);
}
TAxis::TAxis(Int_t nbins,const Double_t *xbins): TNamed(), TAttAxis()
{
fParent = 0;
fLabels = 0;
Set(nbins,xbins);
}
TAxis::~TAxis()
{
if (fLabels) {
fLabels->Delete();
delete fLabels;
fLabels = 0;
}
}
TAxis::TAxis(const TAxis &axis) : TNamed(axis), TAttAxis(axis), fLabels(0)
{
axis.Copy(*this);
}
TAxis& TAxis::operator=(const TAxis &orig)
{
orig.Copy( *this );
return *this;
}
void TAxis::CenterLabels(Bool_t center)
{
if (center) SetBit(kCenterLabels);
else ResetBit(kCenterLabels);
}
Bool_t TAxis::GetCenterLabels() const
{
return TestBit(kCenterLabels) ? kTRUE : kFALSE;
}
void TAxis::CenterTitle(Bool_t center)
{
if (center) SetBit(kCenterTitle);
else ResetBit(kCenterTitle);
}
Bool_t TAxis::GetCenterTitle() const
{
return TestBit(kCenterTitle) ? kTRUE : kFALSE;
}
const char *TAxis::ChooseTimeFormat(Double_t axislength)
{
const char *formatstr;
Int_t reasformat = 0;
Int_t ndiv,nx1,nx2,n;
Double_t awidth;
Double_t length;
if (!axislength) {
length = gPad->GetUxmax() - gPad->GetUxmin();
} else {
length = axislength;
}
ndiv = GetNdivisions();
if (ndiv > 1000) {
nx2 = ndiv/100;
nx1 = TMath::Max(1, ndiv%100);
ndiv = 100*nx2 + Int_t(Double_t(nx1)*gPad->GetAbsWNDC());
}
ndiv = TMath::Abs(ndiv);
n = ndiv - (ndiv/100)*100;
awidth = length/n;
if (awidth>=.5) {
reasformat = 1;
if (awidth>=30) {
awidth /= 60; reasformat = 2;
if (awidth>=30) {
awidth /=60; reasformat = 3;
if (awidth>=12) {
awidth /= 24; reasformat = 4;
if (awidth>=15.218425) {
awidth /= 30.43685; reasformat = 5;
if (awidth>=6) {
awidth /= 12; reasformat = 6;
if (awidth>=2) {
awidth /= 12; reasformat = 7;
}
}
}
}
}
}
}
switch (reasformat) {
case 0:
formatstr = "%S";
break;
case 1:
formatstr = "%Mm%S";
break;
case 2:
formatstr = "%Hh%M";
break;
case 3:
formatstr = "%d-%Hh";
break;
case 4:
formatstr = "%d/%m";
break;
case 5:
formatstr = "%d/%m/%y";
break;
case 6:
formatstr = "%d/%m/%y";
break;
case 7:
formatstr = "%m/%y";
break;
}
return formatstr;
}
void TAxis::Copy(TObject &obj) const
{
TNamed::Copy(obj);
TAttAxis::Copy(((TAxis&)obj));
TAxis &axis( ((TAxis&)obj) );
axis.fNbins = fNbins;
axis.fXmin = fXmin;
axis.fXmax = fXmax;
axis.fFirst = fFirst;
axis.fLast = fLast;
axis.fBits2 = fBits2;
fXbins.Copy(axis.fXbins);
axis.fTimeFormat = fTimeFormat;
axis.fTimeDisplay = fTimeDisplay;
axis.fParent = fParent;
if (axis.fLabels) {
axis.fLabels->Delete();
delete axis.fLabels;
axis.fLabels = 0;
}
if (fLabels) {
TIter next(fLabels);
TObjString *label;
if(! axis.fLabels) {
axis.fLabels = new THashList(axis.fNbins, 3);
}
while( (label=(TObjString*)next()) ) {
TObjString *copyLabel = new TObjString(*label);
axis.fLabels->Add(copyLabel);
copyLabel->SetUniqueID(label->GetUniqueID());
}
}
}
Int_t TAxis::DistancetoPrimitive(Int_t, Int_t)
{
return 9999;
}
void TAxis::ExecuteEvent(Int_t event, Int_t px, Int_t py)
{
if (!gPad) return;
gPad->ExecuteEventAxis(event,px,py,this);
}
Int_t TAxis::FindBin(Double_t x)
{
Int_t bin;
if (x < fXmin) {
bin = 0;
if (fParent == 0) return bin;
if (!fParent->TestBit(TH1::kCanRebin)) return bin;
((TH1*)fParent)->RebinAxis(x,this);
return FindFixBin(x);
} else if ( !(x < fXmax)) {
bin = fNbins+1;
if (fParent == 0) return bin;
if (!fParent->TestBit(TH1::kCanRebin)) return bin;
((TH1*)fParent)->RebinAxis(x,this);
return FindFixBin(x);
} else {
if (!fXbins.fN) {
bin = 1 + int (fNbins*(x-fXmin)/(fXmax-fXmin) );
} else {
bin = 1 + TMath::BinarySearch(fXbins.fN,fXbins.fArray,x);
}
}
return bin;
}
Int_t TAxis::FindBin(const char *label)
{
if (!fLabels) {
if (!fParent) return -1;
fLabels = new THashList(1,1);
fParent->SetBit(TH1::kCanRebin);
if (fXmax <= fXmin) {
fXmin = 0;
fXmax = fNbins;
}
}
TObjString *obj = (TObjString*)fLabels->FindObject(label);
if (obj) return (Int_t)obj->GetUniqueID();
if (!fParent->TestBit(TH1::kCanRebin)) {
if (gDebug>0)
Info("FindBin","Label %s is not in the list and the axis cannot be rebinned - the entry will be added in the underflow bin",label);
return 0;
}
Int_t n = fLabels->GetEntries();
TH1 *h = (TH1*)fParent;
if (n >= fNbins) h->LabelsInflate(GetName());
obj = new TObjString(label);
fLabels->Add(obj);
obj->SetUniqueID(n+1);
return n+1;
}
Int_t TAxis::FindFixBin(Double_t x) const
{
Int_t bin;
if (x < fXmin) {
bin = 0;
} else if ( !(x < fXmax)) {
bin = fNbins+1;
} else {
if (!fXbins.fN) {
bin = 1 + int (fNbins*(x-fXmin)/(fXmax-fXmin) );
} else {
bin = 1 + TMath::BinarySearch(fXbins.fN,fXbins.fArray,x);
}
}
return bin;
}
const char *TAxis::GetBinLabel(Int_t bin) const
{
if (!fLabels) return "";
if (bin <= 0 || bin > fNbins) return "";
TIter next(fLabels);
TObjString *obj;
while ((obj=(TObjString*)next())) {
Int_t binid = (Int_t)obj->GetUniqueID();
if (binid == bin) return obj->GetName();
}
return "";
}
Int_t TAxis::GetFirst() const
{
if (!TestBit(kAxisRange)) return 1;
return fFirst;
}
Int_t TAxis::GetLast() const
{
if (!TestBit(kAxisRange)) return fNbins;
return fLast;
}
Double_t TAxis::GetBinCenter(Int_t bin) const
{
Double_t binwidth;
if (!fXbins.fN || bin<1 || bin>fNbins) {
binwidth = (fXmax - fXmin) / Double_t(fNbins);
return fXmin + (bin-1) * binwidth + 0.5*binwidth;
} else {
binwidth = fXbins.fArray[bin] - fXbins.fArray[bin-1];
return fXbins.fArray[bin-1] + 0.5*binwidth;
}
}
Double_t TAxis::GetBinCenterLog(Int_t bin) const
{
Double_t low,up;
if (!fXbins.fN || bin<1 || bin>fNbins) {
Double_t binwidth = (fXmax - fXmin) / Double_t(fNbins);
low = fXmin + (bin-1) * binwidth;
up = low+binwidth;
} else {
low = fXbins.fArray[bin-1];
up = fXbins.fArray[bin];
}
if (low <=0 ) return GetBinCenter(bin);
return TMath::Sqrt(low*up);
}
Double_t TAxis::GetBinLowEdge(Int_t bin) const
{
if (fXbins.fN && bin > 0 && bin <=fNbins) return fXbins.fArray[bin-1];
Double_t binwidth = (fXmax - fXmin) / Double_t(fNbins);
return fXmin + (bin-1) * binwidth;
}
Double_t TAxis::GetBinUpEdge(Int_t bin) const
{
if (!fXbins.fN || bin < 1 || bin>fNbins) {
Double_t binwidth = (fXmax - fXmin) / Double_t(fNbins);
return fXmin + bin*binwidth;
}
return fXbins.fArray[bin];
}
Double_t TAxis::GetBinWidth(Int_t bin) const
{
if (fNbins <= 0) return 0;
if (fXbins.fN <= 0) return (fXmax - fXmin) / Double_t(fNbins);
if (bin >fNbins) bin = fNbins;
if (bin <1 ) bin = 1;
return fXbins.fArray[bin] - fXbins.fArray[bin-1];
}
void TAxis::GetCenter(Double_t *center) const
{
Int_t bin;
for (bin=1; bin<=fNbins; bin++) *(center + bin-1) = GetBinCenter(bin);
}
void TAxis::GetLowEdge(Double_t *edge) const
{
Int_t bin;
for (bin=1; bin<=fNbins; bin++) *(edge + bin-1) = GetBinLowEdge(bin);
}
const char *TAxis::GetTimeFormatOnly() const
{
static TString timeformat;
Int_t idF = fTimeFormat.Index("%F");
if (idF>=0) {
timeformat = fTimeFormat(0,idF);
} else {
timeformat = fTimeFormat;
}
return timeformat.Data();
}
const char *TAxis::GetTicks() const
{
if (TestBit(kTickPlus) && TestBit(kTickMinus)) return "+-";
if (TestBit(kTickMinus)) return "-";
return "+";
}
void TAxis::LabelsOption(Option_t *option)
{
if (!fLabels) {
Warning("Sort","Cannot sort. No labels");
return;
}
TH1 *h = (TH1*)GetParent();
if (!h) {
Error("Sort","Axis has no parent");
return;
}
h->LabelsOption(option,GetName());
}
Bool_t TAxis::GetRotateTitle() const
{
return TestBit(kRotateTitle) ? kTRUE : kFALSE;
}
void TAxis::ImportAttributes(const TAxis *axis)
{
SetTitle(axis->GetTitle());
SetNdivisions(axis->GetNdivisions());
SetAxisColor(axis->GetAxisColor());
SetLabelColor(axis->GetLabelColor());
SetLabelFont(axis->GetLabelFont());
SetLabelOffset(axis->GetLabelOffset());
SetLabelSize(axis->GetLabelSize());
SetTickLength(axis->GetTickLength());
SetTitleOffset(axis->GetTitleOffset());
SetTitleSize(axis->GetTitleSize());
SetTitleColor(axis->GetTitleColor());
SetTitleFont(axis->GetTitleFont());
SetBit(TAxis::kCenterTitle, axis->TestBit(TAxis::kCenterTitle));
SetBit(TAxis::kCenterLabels, axis->TestBit(TAxis::kCenterLabels));
SetBit(TAxis::kRotateTitle, axis->TestBit(TAxis::kRotateTitle));
SetBit(TAxis::kNoExponent, axis->TestBit(TAxis::kNoExponent));
SetBit(TAxis::kTickPlus, axis->TestBit(TAxis::kTickPlus));
SetBit(TAxis::kTickMinus, axis->TestBit(TAxis::kTickMinus));
SetBit(TAxis::kMoreLogLabels, axis->TestBit(TAxis::kMoreLogLabels));
if (axis->GetDecimals()) SetBit(TAxis::kDecimals);
SetTimeFormat(axis->GetTimeFormat());
}
void TAxis::RotateTitle(Bool_t rotate)
{
if (rotate) SetBit(kRotateTitle);
else ResetBit(kRotateTitle);
}
void TAxis::SaveAttributes(std::ostream &out, const char *name, const char *subname)
{
char quote = '"';
if (strlen(GetTitle())) {
TString t(GetTitle());
t.ReplaceAll("\\","\\\\");
out<<" "<<name<<subname<<"->SetTitle("<<quote<<t.Data()<<quote<<");"<<std::endl;
}
if (fTimeDisplay) {
out<<" "<<name<<subname<<"->SetTimeDisplay(1);"<<std::endl;
out<<" "<<name<<subname<<"->SetTimeFormat("<<quote<<GetTimeFormat()<<quote<<");"<<std::endl;
}
if (fLabels) {
TIter next(fLabels);
TObjString *obj;
while ((obj=(TObjString*)next())) {
out<<" "<<name<<subname<<"->SetBinLabel("<<obj->GetUniqueID()<<","<<quote<<obj->GetName()<<quote<<");"<<std::endl;
}
}
if (fFirst || fLast) {
out<<" "<<name<<subname<<"->SetRange("<<fFirst<<","<<fLast<<");"<<std::endl;
}
if (TestBit(kLabelsHori)) {
out<<" "<<name<<subname<<"->SetBit(TAxis::kLabelsHori);"<<std::endl;
}
if (TestBit(kLabelsVert)) {
out<<" "<<name<<subname<<"->SetBit(TAxis::kLabelsVert);"<<std::endl;
}
if (TestBit(kLabelsDown)) {
out<<" "<<name<<subname<<"->SetBit(TAxis::kLabelsDown);"<<std::endl;
}
if (TestBit(kLabelsUp)) {
out<<" "<<name<<subname<<"->SetBit(TAxis::kLabelsUp);"<<std::endl;
}
if (TestBit(kCenterTitle)) {
out<<" "<<name<<subname<<"->CenterTitle(true);"<<std::endl;
}
if (TestBit(kRotateTitle)) {
out<<" "<<name<<subname<<"->RotateTitle(true);"<<std::endl;
}
if (TestBit(kMoreLogLabels)) {
out<<" "<<name<<subname<<"->SetMoreLogLabels();"<<std::endl;
}
if (TestBit(kNoExponent)) {
out<<" "<<name<<subname<<"->SetNoExponent();"<<std::endl;
}
TAttAxis::SaveAttributes(out,name,subname);
}
void TAxis::Set(Int_t nbins, Double_t xlow, Double_t xup)
{
fNbins = nbins;
fXmin = xlow;
fXmax = xup;
if (!fParent) SetDefaults();
if (fXbins.fN > 0) fXbins.Set(0);
}
void TAxis::Set(Int_t nbins, const Float_t *xbins)
{
Int_t bin;
fNbins = nbins;
fXbins.Set(fNbins+1);
for (bin=0; bin<= fNbins; bin++)
fXbins.fArray[bin] = xbins[bin];
for (bin=1; bin<= fNbins; bin++)
if (fXbins.fArray[bin] < fXbins.fArray[bin-1])
Error("TAxis::Set", "bins must be in increasing order");
fXmin = fXbins.fArray[0];
fXmax = fXbins.fArray[fNbins];
if (!fParent) SetDefaults();
}
void TAxis::Set(Int_t nbins, const Double_t *xbins)
{
Int_t bin;
fNbins = nbins;
fXbins.Set(fNbins+1);
for (bin=0; bin<= fNbins; bin++)
fXbins.fArray[bin] = xbins[bin];
for (bin=1; bin<= fNbins; bin++)
if (fXbins.fArray[bin] < fXbins.fArray[bin-1])
Error("TAxis::Set", "bins must be in increasing order");
fXmin = fXbins.fArray[0];
fXmax = fXbins.fArray[fNbins];
if (!fParent) SetDefaults();
}
void TAxis::SetDefaults()
{
fFirst = 0;
fLast = 0;
fBits2 = 0;
char name[2];
strlcpy(name,GetName(),2);
name[1] = 0;
TAttAxis::ResetAttAxis(name);
fTimeDisplay = 0;
SetTimeFormat();
}
Bool_t TAxis::GetDecimals() const
{
if ((fBits2 & kDecimals) != 0) return kTRUE;
else return kFALSE;
}
void TAxis::SetDecimals(Bool_t dot)
{
if (dot) fBits2 |= kDecimals;
else fBits2 &= ~kDecimals;
}
void TAxis::SetBinLabel(Int_t bin, const char *label)
{
if (!fLabels) fLabels = new THashList(fNbins,3);
if (bin <= 0 || bin > fNbins) {
Error("SetBinLabel","Illegal bin number: %d",bin);
return;
}
TIter next(fLabels);
TObjString *obj;
while ((obj=(TObjString*)next())) {
if ( obj->GetUniqueID()==(UInt_t)bin ) {
obj->SetString(label);
fLabels->Rehash(fLabels->GetSize() );
return;
}
}
obj = new TObjString(label);
fLabels->Add(obj);
obj->SetUniqueID((UInt_t)bin);
}
void TAxis::SetLimits(Double_t xmin, Double_t xmax)
{
fXmin = xmin;
fXmax = xmax;
}
Bool_t TAxis::GetMoreLogLabels() const
{
return TestBit(kMoreLogLabels) ? kTRUE : kFALSE;
}
void TAxis::SetMoreLogLabels(Bool_t more)
{
if (more) SetBit(kMoreLogLabels);
else ResetBit(kMoreLogLabels);
}
Bool_t TAxis::GetNoExponent() const
{
return TestBit(kNoExponent) ? kTRUE : kFALSE;
}
void TAxis::SetNoExponent(Bool_t noExponent)
{
if (noExponent) SetBit(kNoExponent);
else ResetBit(kNoExponent);
}
void TAxis::SetRange(Int_t first, Int_t last)
{
Int_t nCells = fNbins + 1;
if (last < first || (first < 0 && last < 0) ||
(first > nCells && last > nCells) || (first == 0 && last == 0)
) {
fFirst = 1;
fLast = fNbins;
SetBit(kAxisRange, 0);
} else {
fFirst = std::max(first, 0);
fLast = std::min(last, nCells);
SetBit(kAxisRange, 1);
}
}
void TAxis::SetRangeUser(Double_t ufirst, Double_t ulast)
{
if (!strstr(GetName(),"xaxis")) {
TH1 *hobj = (TH1*)GetParent();
if (hobj &&
((hobj->GetDimension() == 2 && strstr(GetName(),"zaxis"))
|| (hobj->GetDimension() == 1 && strstr(GetName(),"yaxis")))) {
hobj->SetMinimum(ufirst);
hobj->SetMaximum(ulast);
return;
}
}
Int_t ifirst = FindFixBin(ufirst);
Int_t ilast = FindFixBin(ulast);
if (GetBinUpEdge(ifirst) <= ufirst ) ifirst += 1;
if (GetBinLowEdge(ilast) >= ulast ) ilast -= 1;
SetRange(ifirst, ilast);
}
void TAxis::SetTicks(Option_t *option)
{
ResetBit(kTickPlus);
ResetBit(kTickMinus);
if (strchr(option,'+')) SetBit(kTickPlus);
if (strchr(option,'-')) SetBit(kTickMinus);
}
void TAxis::SetTimeFormat(const char *tformat)
{
TString timeformat = tformat;
if (timeformat.Index("%F")>=0 || timeformat.IsNull()) {
fTimeFormat = timeformat;
return;
}
Int_t idF = fTimeFormat.Index("%F");
if (idF>=0) {
Int_t lnF = fTimeFormat.Length();
TString stringtimeoffset = fTimeFormat(idF,lnF);
fTimeFormat = tformat;
fTimeFormat.Append(stringtimeoffset);
} else {
fTimeFormat = tformat;
SetTimeOffset(gStyle->GetTimeOffset());
}
}
void TAxis::SetTimeOffset(Double_t toffset, Option_t *option)
{
TString opt = option;
opt.ToLower();
char tmp[20];
time_t timeoff;
struct tm* utctis;
Int_t idF = fTimeFormat.Index("%F");
if (idF>=0) fTimeFormat.Remove(idF);
fTimeFormat.Append("%F");
timeoff = (time_t)((Long_t)(toffset));
utctis = gmtime(&timeoff);
strftime(tmp,20,"%Y-%m-%d %H:%M:%S",utctis);
fTimeFormat.Append(tmp);
Double_t ds = toffset-(Int_t)toffset;
snprintf(tmp,20,"s%g",ds);
fTimeFormat.Append(tmp);
if (opt.Contains("gmt")) fTimeFormat.Append(" GMT");
}
void TAxis::Streamer(TBuffer &R__b)
{
if (R__b.IsReading()) {
UInt_t R__s, R__c;
Version_t R__v = R__b.ReadVersion(&R__s, &R__c);
if (R__v > 5) {
R__b.ReadClassBuffer(TAxis::Class(), this, R__v, R__s, R__c);
return;
}
TNamed::Streamer(R__b);
TAttAxis::Streamer(R__b);
R__b >> fNbins;
if (R__v < 5) {
Float_t xmin,xmax;
R__b >> xmin; fXmin = xmin;
R__b >> xmax; fXmax = xmax;
Float_t *xbins = 0;
Int_t n = R__b.ReadArray(xbins);
fXbins.Set(n);
for (Int_t i=0;i<n;i++) fXbins.fArray[i] = xbins[i];
delete [] xbins;
} else {
R__b >> fXmin;
R__b >> fXmax;
fXbins.Streamer(R__b);
}
if (R__v > 2) {
R__b >> fFirst;
R__b >> fLast;
if (fFirst < 0 || fFirst > fNbins) fFirst = 0;
if (fLast < 0 || fLast > fNbins) fLast = 0;
if (fLast < fFirst) { fFirst = 0; fLast = 0;}
if (fFirst ==0 && fLast == 0) SetBit(kAxisRange,0);
}
if (R__v > 3) {
R__b >> fTimeDisplay;
fTimeFormat.Streamer(R__b);
} else {
SetTimeFormat();
}
R__b.CheckByteCount(R__s, R__c, TAxis::IsA());
} else {
R__b.WriteClassBuffer(TAxis::Class(),this);
}
}
void TAxis::UnZoom()
{
if (!gPad) return;
gPad->SetView();
SetRange(0,0);
TH1 *hobj1 = (TH1*)GetParent();
if (!strstr(GetName(),"xaxis")) {
if (!hobj1) return;
if (hobj1->GetDimension() == 2) {
if (strstr(GetName(),"zaxis")) {
hobj1->SetMinimum();
hobj1->SetMaximum();
hobj1->ResetBit(TH1::kIsZoomed);
}
return;
}
if (strcmp(hobj1->GetName(),"hframe") == 0 ) {
hobj1->SetMinimum(fXmin);
hobj1->SetMaximum(fXmax);
} else {
if (fXmin==hobj1->GetMinimum() && fXmax==hobj1->GetMaximum()) {
hobj1->SetMinimum(fXmin);
hobj1->SetMaximum(fXmax);
} else {
hobj1->SetMinimum();
hobj1->SetMaximum();
}
hobj1->ResetBit(TH1::kIsZoomed);
}
}
TIter next(gPad->GetListOfPrimitives());
TObject *obj;
while ((obj= next())) {
if (!obj->InheritsFrom(TH1::Class())) continue;
TH1 *hobj = (TH1*)obj;
if (hobj == hobj1) continue;
if (!strstr(GetName(),"xaxis")) {
if (hobj->GetDimension() == 2) {
if (strstr(GetName(),"zaxis")) {
hobj->SetMinimum();
hobj->SetMaximum();
hobj->ResetBit(TH1::kIsZoomed);
} else {
hobj->GetYaxis()->SetRange(0,0);
}
return;
}
if (strcmp(hobj->GetName(),"hframe") == 0 ) {
hobj->SetMinimum(fXmin);
hobj->SetMaximum(fXmax);
} else {
hobj->SetMinimum();
hobj->SetMaximum();
hobj->ResetBit(TH1::kIsZoomed);
}
} else {
hobj->GetXaxis()->SetRange(0,0);
}
}
}
void TAxis::ZoomOut(Double_t factor, Double_t offset)
{
if (factor <= 0) factor = 2;
Double_t center = (GetFirst()*(1-offset) + GetLast()*(1+offset))/2.;
Int_t first = int(TMath::Floor(center+(GetFirst()-center)*factor + 0.4999999));
Int_t last = int(TMath::Floor(center+(GetLast() -center)*factor + 0.5000001));
if (first==GetFirst() && last==GetLast()) { first--; last++; }
SetRange(first,last);
}