#include "TRecorder.h"
#include "TROOT.h"
#include "TFile.h"
#include "TTimer.h"
#include "TTree.h"
#include "TMutex.h"
#include "TGButton.h"
#include "TGFileDialog.h"
#include "TGLabel.h"
#include "TGWindow.h"
#include "Buttons.h"
#include "TKey.h"
#include "TPaveLabel.h"
#include "TLatex.h"
const char *kRecEventNames[] = {
"KeyPress",
"KeyRelease",
"ButtonPress",
"ButtonRelease",
"MotionNotify",
"EnterNotify",
"LeaveNotify",
"FocusIn",
"FocusOut",
"Expose",
"ConfigureNotify",
"MapNotify",
"UnmapNotify",
"DestroyNotify",
"ClientMessage",
"SelectionClear",
"SelectionRequest",
"SelectionNotify",
"ColormapNotify",
"ButtonDoubleClick",
"OtherEvent"
};
const char *kCmdEventTree = "CmdEvents";
const char *kGuiEventTree = "GuiEvents";
const char *kWindowsTree = "WindowsTree";
const char *kExtraEventTree = "ExtraEvents";
const char *kBranchName = "MainBranch";
ClassImp(TRecorder)
TRecorder::TRecorder()
{
fFilename = "";
fRecorderState = new TRecorderInactive();
}
TRecorder::TRecorder(const char *filename, Option_t *option)
{
TString opt(option);
fFilename = "";
fRecorderState = new TRecorderInactive();
if ((opt == "NEW") || (opt == "RECREATE"))
Start(filename, option);
else
Replay(filename);
}
TRecorder::~TRecorder()
{
delete fRecorderState;
}
void TRecorder::Browse(TBrowser *)
{
Replay(fFilename);
}
void TRecorder::Start(const char *filename, Option_t *option, Window_t *w, Int_t winCount)
{
fRecorderState->Start(this, filename, option, w, winCount);
}
void TRecorder::Stop(Bool_t guiCommand)
{
fRecorderState->Stop(this, guiCommand);
}
Bool_t TRecorder::Replay(const char *filename, Bool_t showMouseCursor, TRecorder::EReplayModes mode)
{
return fRecorderState->Replay(this, filename, showMouseCursor, mode);
}
void TRecorder::Pause()
{
fRecorderState->Pause(this);
}
void TRecorder::Resume()
{
fRecorderState->Resume(this);
}
void TRecorder::ReplayStop()
{
fRecorderState->ReplayStop(this);
}
void TRecorder::ListCmd(const char *filename)
{
fRecorderState->ListCmd(filename);
}
void TRecorder::ListGui(const char *filename)
{
fRecorderState->ListGui(filename);
}
void TRecorder::ChangeState(TRecorderState *newstate, Bool_t deletePreviousState)
{
if (deletePreviousState)
delete fRecorderState;
fRecorderState = newstate;
}
TRecorder::ERecorderState TRecorder::GetState() const
{
return fRecorderState->GetState();
}
void TRecorder::PrevCanvases(const char *filename, Option_t *option)
{
fRecorderState->PrevCanvases(filename,option);
}
ClassImp(TRecorderReplaying)
TRecorderReplaying::TRecorderReplaying(const char *filename)
{
fFile = TFile::Open(filename);
fCmdEvent = new TRecCmdEvent();
fGuiEvent = new TRecGuiEvent();
fExtraEvent = new TRecExtraEvent();
fWindowList = new TList();
fTimer = new TTimer();
fMutex = new TMutex(kFALSE);
}
TRecorderReplaying::~TRecorderReplaying()
{
fTimer->Disconnect(fTimer, "Timeout()", this, "ReplayRealtime()");
fTimer->TurnOff();
gClient->Disconnect(gClient, "RegisteredWindow(Window_t)", this, "WaitForWindow(Window_t)");
gClient->Disconnect(gClient, "RegisteredWindow(Window_t)", this, "RegisterWindow(Window_t)");
if (fFile) {
fFile->Close();
delete fFile;
}
delete fWindowList;
delete fCmdEvent;
delete fGuiEvent;
delete fExtraEvent;
delete fMutex;
}
Bool_t TRecorderReplaying::Initialize(TRecorder *r, Bool_t showMouseCursor, TRecorder::EReplayModes)
{
fWin = 0;
fGuiTreeCounter = 0;
fCmdTreeCounter = 0;
fExtraTreeCounter = 0;
fRegWinCounter = 0;
fRecorder = 0;
fFilterStatusBar = kFALSE;
fWaitingForWindow = kFALSE;
fEventReplayed = 1;
fRecorder = r;
fShowMouseCursor = showMouseCursor;
if (!fFile || fFile->IsZombie() || !fFile->IsOpen())
return kFALSE;
fCmdTree = (TTree*) fFile->Get(kCmdEventTree);
fWinTree = (TTree*) fFile->Get(kWindowsTree);
fGuiTree = (TTree*) fFile->Get(kGuiEventTree);
fExtraTree = (TTree*) fFile->Get(kExtraEventTree);
if (!fCmdTree || !fWinTree || ! fGuiTree || ! fExtraTree) {
Error("TRecorderReplaying::Initialize", "The ROOT file is not valid event logfile.");
return kFALSE;
}
try {
fCmdTree->SetBranchAddress(kBranchName, &fCmdEvent);
fWinTree->SetBranchAddress(kBranchName, &fWin);
fGuiTree->SetBranchAddress(kBranchName, &fGuiEvent);
fExtraTree->SetBranchAddress(kBranchName, &fExtraEvent);
}
catch(...) {
Error("TRecorderReplaying::Initialize", "The ROOT file is not valid event logfile");
return kFALSE;
}
if (!PrepareNextEvent()) {
Info("TRecorderReplaying::Initialize", "Log file empty. No event to replay.");
return kFALSE;
}
fWinTreeEntries = fWinTree->GetEntries();
gClient->Connect("RegisteredWindow(Window_t)", "TRecorderReplaying", this, "RegisterWindow(Window_t)");
Info("TRecorderReplaying::Initialize", "Replaying of file %s started", fFile->GetName());
TFile *f = TFile::Open(fFile->GetName());
TIter nextkey(f->GetListOfKeys());
TKey *key;
TObject *obj;
while ((key = (TKey*)nextkey())) {
fFilterStatusBar = kTRUE;
obj = key->ReadObj();
if (!obj->InheritsFrom("TCanvas"))
continue;
fCanv = (TCanvas*) obj;
fCanv->Draw();
}
TCanvas *canvas;
TIter nextc(gROOT->GetListOfCanvases());
while ((canvas = (TCanvas*)nextc())) {
canvas->SetWindowSize(canvas->GetWindowWidth(), canvas->GetWindowHeight());
}
fFilterStatusBar = kFALSE;
f->Close();
gPad = 0;
fTimer->Connect("Timeout()", "TRecorderReplaying", this, "ReplayRealtime()");
fTimer->Start(0);
return kTRUE;
}
void TRecorderReplaying::RegisterWindow(Window_t w)
{
if (fFilterStatusBar) {
TGWindow *win = gClient->GetWindowById(w);
if (win) {
if (win->GetParent()->InheritsFrom("TGStatusBar")) {
fFilterStatusBar = kFALSE;
return;
}
}
}
if (fWinTreeEntries > fRegWinCounter) {
fWinTree->GetEntry(fRegWinCounter);
}
else {
Error("TRecorderReplaying::RegisterWindow", "More windows registered than expected");
return;
}
if ((gDebug > 0) && (fWaitingForWindow)) {
cout << " Window registered: new ID: " << hex << w << " previous ID: " << fWin << endl;
}
fMutex->Lock();
fRegWinCounter++;
TRecWinPair *ids = new TRecWinPair(fWin, w);
fWindowList->Add(ids);
if (fWaitingForWindow && fGuiEvent->fWindow == fWin) {
if (gDebug > 0)
cout << " Window " << hex << fGuiEvent->fWindow << " registered." << endl;
fNextEvent = fGuiEvent;
fWaitingForWindow = kFALSE;
fTimer->Start(25);
}
fMutex->UnLock();
}
Bool_t TRecorderReplaying::RemapWindowReferences()
{
fMutex->Lock();
TRecWinPair *ids;
TListIter it(fWindowList);
Bool_t found = kFALSE;
while ((ids = (TRecWinPair*)it.Next())) {
if (ids->fKey == fGuiEvent->fWindow) {
fGuiEvent->fWindow = ids->fValue;
found = kTRUE;
}
for (Int_t i = 0; i < 5; ++i) {
if ((Long_t) ids->fKey == fGuiEvent->fUser[i])
fGuiEvent->fUser[i] = ids->fValue;
}
if (fGuiEvent->fMasked && ids->fKey == fGuiEvent->fMasked)
fGuiEvent->fMasked = ids->fValue;
}
if(found) {
fMutex->UnLock();
return kTRUE;
}
if (gDebug > 0) {
cout << "fGuiTreeCounter = " << dec << fGuiTreeCounter << " No mapping found for ID " << hex << fGuiEvent->fWindow << endl;
TRecorderInactive::DumpRootEvent(fGuiEvent,0);
}
fTimer->Stop();
fWaitingForWindow = kTRUE;
fMutex->UnLock();
return kFALSE;
}
Bool_t TRecorderReplaying::FilterEvent(TRecGuiEvent *e)
{
if (e->fType == kClientMessage) {
if (!((e->fFormat == 32) && ((Atom_t)e->fUser[0] == gWM_DELETE_WINDOW) &&
(e->fHandle != gROOT_MESSAGE)))
return kTRUE;
else
return kFALSE;
}
if (e->fType == kConfigureNotify && e->fUser[4] == TRecGuiEvent::kCNFilter) {
return kTRUE;
}
if (e->fType == kOtherEvent)
return kTRUE;
return kFALSE;
}
Bool_t TRecorderReplaying::PrepareNextEvent()
{
fCmdEvent = 0;
fGuiEvent = 0;
fExtraEvent = 0;
fNextEvent = 0;
if (fCmdTree->GetEntries() > fCmdTreeCounter)
fCmdTree->GetEntry(fCmdTreeCounter);
if (fExtraTree->GetEntries() > fExtraTreeCounter)
fExtraTree->GetEntry(fExtraTreeCounter);
while (fGuiTree->GetEntries() > fGuiTreeCounter) {
fGuiTree->GetEntry(fGuiTreeCounter);
if (!FilterEvent(fGuiEvent))
break;
fGuiTreeCounter++;
}
if (fCmdEvent && fGuiEvent && fExtraEvent) {
if ((fCmdEvent->GetTime() <= fGuiEvent->GetTime()) &&
(fCmdEvent->GetTime() <= fExtraEvent->GetTime()))
fNextEvent = fCmdEvent;
else {
if (fGuiEvent->GetTime() <= fExtraEvent->GetTime())
fNextEvent = fGuiEvent;
else
fNextEvent = fExtraEvent;
}
}
else if (fCmdEvent && fGuiEvent) {
if (fCmdEvent->GetTime() <= fGuiEvent->GetTime())
fNextEvent = fCmdEvent;
else
fNextEvent = fGuiEvent;
}
else if (fCmdEvent && fExtraEvent ) {
if (fCmdEvent->GetTime() <= fExtraEvent->GetTime())
fNextEvent = fCmdEvent;
else
fNextEvent = fExtraEvent;
}
else if (fGuiEvent && fExtraEvent) {
if (fExtraEvent->GetTime() <= fGuiEvent->GetTime())
fNextEvent = fExtraEvent;
else
fNextEvent = fGuiEvent;
}
else if (!fCmdEvent && !fGuiEvent && !fExtraEvent)
fNextEvent = 0;
else if (fGuiEvent)
fNextEvent = fGuiEvent;
else if (fCmdEvent)
fNextEvent = fCmdEvent;
else
fNextEvent = fExtraEvent;
if (fNextEvent == 0)
return kFALSE;
if (fNextEvent == fCmdEvent)
fCmdTreeCounter++;
if (fNextEvent == fExtraEvent)
fExtraTreeCounter++;
if (fNextEvent == fGuiEvent) {
if (RemapWindowReferences())
fGuiTreeCounter++;
else
fNextEvent = 0;
}
return kTRUE;
}
Bool_t TRecorderReplaying::CanOverlap()
{
if (!fGuiEvent) {
Error("TRecorderReplaying::CanOverlap()", "fGuiEvent = 0");
return kFALSE;
}
if (fNextEvent->GetType() == TRecEvent::kCmdEvent)
return kFALSE;
if (gDebug > 0) {
cout << "Event overlapping " << kRecEventNames[((TRecGuiEvent*)fNextEvent)->fType] << endl;
TRecorderInactive::DumpRootEvent(((TRecGuiEvent*)fNextEvent), 0);
}
TRecGuiEvent *e = (TRecGuiEvent*) fNextEvent;
if (e->fType == kButtonPress)
return kTRUE;
if (e->fType == kButtonRelease)
return kTRUE;
return kFALSE;
}
void TRecorderReplaying::ReplayRealtime()
{
if ((gROOT->GetEditorMode() == kText) ||
(gROOT->GetEditorMode() == kPaveLabel)){
gROOT->SetEditorMode();
}
if (gVirtualX->EventsPending())
return;
if (!fEventReplayed && !CanOverlap())
return;
if (fNextEvent) {
fEventReplayed = 0;
fPreviousEventTime = fNextEvent->GetTime();
fNextEvent->ReplayEvent(fShowMouseCursor);
fEventReplayed = 1;
}
if (!PrepareNextEvent()) {
Info("TRecorderReplaying::ReplayRealtime", "Replaying finished");
fRecorder->ChangeState(new TRecorderInactive());
return;
}
else {
if (fNextEvent) {
ULong_t difference = (ULong_t) (fNextEvent->GetTime() - fPreviousEventTime);
fTimer->Start(difference);
}
}
}
void TRecorderReplaying::Pause(TRecorder *r)
{
fTimer->Stop();
r->ChangeState(new TRecorderPaused(this), kFALSE);
Info("TRecorderReplaying::Pause", "Replaying paused.");
}
void TRecorderReplaying::ReplayStop(TRecorder *r)
{
Info("TRecorderReplaying::ReplayStop", "Replaying cancelled");
r->ChangeState(new TRecorderInactive());
}
void TRecorderReplaying::Continue()
{
if (fNextEvent)
fTimer->Start((ULong_t) (fNextEvent->GetTime() - fPreviousEventTime));
}
ClassImp(TRecorderInactive)
void TRecorderInactive::Start(TRecorder *r, const char *filename, Option_t *option, Window_t *w, Int_t winCount)
{
TRecorderRecording *rec = new TRecorderRecording(r, filename, option, w, winCount);
if (rec->StartRecording()) {
r->ChangeState(rec);
r->fFilename = gSystem->BaseName(filename);
}
else
delete rec;
}
Bool_t TRecorderInactive::Replay(TRecorder *r, const char *filename, Bool_t showMouseCursor, TRecorder::EReplayModes mode)
{
TRecorderReplaying *replay = new TRecorderReplaying(filename);
if (replay->Initialize(r, showMouseCursor, mode)) {
r->ChangeState(replay);
r->fFilename = gSystem->BaseName(filename);
return kTRUE;
}
else {
delete replay;
return kFALSE;
}
}
void TRecorderInactive::ListCmd(const char *filename)
{
TFile *file = TFile::Open(filename);
if (file->IsZombie() || !file->IsOpen()) {
delete file;
return;
}
TTree *t1 = (TTree*)file->Get(kCmdEventTree);
if (!t1) {
Error("TRecorderInactive::List", "The ROOT file is not valid event logfile.");
delete file;
return;
}
TRecCmdEvent *fCmdEvent = new TRecCmdEvent();
t1->SetBranchAddress(kBranchName, &fCmdEvent);
Int_t entries = t1->GetEntries();
for (Int_t i = 0; i < entries; ++i) {
t1->GetEntry(i);
cout << "[" << i << "] " << "fTime=" << (ULong_t) fCmdEvent->GetTime() << " fText=" << fCmdEvent->GetText() << endl;
}
cout << endl;
delete fCmdEvent;
delete file;
}
void TRecorderInactive::ListGui(const char *filename)
{
TFile *file = TFile::Open(filename);
if (file->IsZombie() || !file->IsOpen()) {
delete file;
return;
}
TTree *t1 = (TTree*)file->Get(kGuiEventTree);
if (!t1) {
Error("TRecorderInactive::ListGui", "The ROOT file is not valid event logfile.");
delete file;
return;
}
TRecGuiEvent *guiEvent = new TRecGuiEvent();
t1->SetBranchAddress(kBranchName, &guiEvent);
Int_t entries = t1->GetEntries();
for (Int_t i = 0; i < entries ; ++i) {
t1->GetEntry(i);
DumpRootEvent(guiEvent, i);
}
delete file;
delete guiEvent;
}
void TRecorderInactive::DumpRootEvent(TRecGuiEvent *e, Int_t n)
{
cout << "[" << n << "] " << dec << setw(10) << e->GetTime().AsString() << setw(15) << kRecEventNames[e->fType]
<< " fW:" << hex << e->fWindow
<< " t:" << dec << e->fTime
<< " x:" << DisplayValid(e->fX) << " y:" << DisplayValid(e->fY)
<< " fXR:" << DisplayValid(e->fXRoot) << " fYR:" << DisplayValid(e->fYRoot)
<< " c:" << DisplayValid(e->fCode)
<< " s:" << DisplayValid(e->fState)
<< " w:" << DisplayValid(e->fWidth) << " h:" << DisplayValid(e->fHeight)
<< " cnt:" << DisplayValid(e->fCount)
<< " se:" << e->fSendEvent
<< " h:" << e->fHandle
<< " fF:" << DisplayValid(e->fFormat) << " | ";
for (Int_t i=0; i<5; ++i)
if (DisplayValid(e->fUser[i]) != -1)
cout << "[" << i << "]=" << DisplayValid(e->fUser[i]);
if (e->fMasked)
cout << " | fM:" << hex << e->fMasked;
cout << endl;
}
void TRecorderInactive::PrevCanvases(const char *filename, Option_t *option)
{
fCollect = gROOT->GetListOfCanvases();
TFile *f = TFile::Open(filename, option);
fCollect->Write();
f->Close();
}
ClassImp(TRecorderPaused)
TRecorderPaused::TRecorderPaused(TRecorderReplaying *state)
{
fReplayingState = state;
}
void TRecorderPaused::Resume(TRecorder *r)
{
fReplayingState->Continue();
Info("TRecorderPaused::Resume", "Replaying resumed");
r->ChangeState(fReplayingState);
}
void TRecorderPaused::ReplayStop(TRecorder *r)
{
delete fReplayingState;
Info("TRecorderReplaying::ReplayStop", "Reaplying cancelled");
r->ChangeState(new TRecorderInactive());
}
ClassImp(TRecorderRecording)
TRecorderRecording::TRecorderRecording(TRecorder *r, const char *filename,
Option_t *option, Window_t *w,
Int_t winCount)
{
fRecorder = r;
fFilteredIdsCount = winCount;
fFilteredIds = new Window_t[fFilteredIdsCount];
for(Int_t i=0; i < fFilteredIdsCount; ++i)
fFilteredIds[i] = w[i];
fCmdEventPending = kFALSE;
fFilterEventPave = kFALSE;
fRegWinCounter = 0;
fTimer = new TTimer(25, kTRUE);
fFile = TFile::Open(filename, option);
fWinTree = new TTree(kWindowsTree, "Windows");
fCmdTree = new TTree(kCmdEventTree, "Commandline events");
fGuiTree = new TTree(kGuiEventTree, "GUI events");
fExtraTree = new TTree(kExtraEventTree, "Extra events");
fWin = 0;
fCmdEvent = new TRecCmdEvent();
fGuiEvent = new TRecGuiEvent();
fExtraEvent = new TRecExtraEvent();
}
TRecorderRecording::~TRecorderRecording()
{
delete[] fFilteredIds;
if (fFile)
delete fFile;
delete fTimer;
delete fCmdEvent;
delete fGuiEvent;
delete fExtraEvent;
}
Bool_t TRecorderRecording::StartRecording()
{
if (!fFile || fFile->IsZombie() || !fFile->IsOpen())
return kFALSE;
gApplication->Connect("LineProcessed(const char*)", "TRecorderRecording", this, "RecordCmdEvent(const char* line)");
gClient->Connect("RegisteredWindow(Window_t)", "TRecorderRecording", this, "RegisterWindow(Window_t)");
gClient->Connect("ProcessedEvent(Event_t*, Window_t)", "TRecorderRecording", this, "RecordGuiEvent(Event_t*, Window_t)");
TQObject::Connect("TGFrame", "ProcessedConfigure(Event_t*)", "TRecorderRecording", this, "RecordGuiCNEvent(Event_t*)");
TQObject::Connect("TPad", "RecordPave(const TObject*)", "TRecorderRecording", this, "RecordPave(const TObject*)");
TQObject::Connect("TPad", "RecordLatex(const TObject*)", "TRecorderRecording", this, "RecordText(const TObject*)");
TQObject::Connect("TPad", "EventPave()", "TRecorderRecording", this, "FilterEventPave()");
TQObject::Connect("TPad", "StartEditing()", "TRecorderRecording", this, "StartEditing()");
fWinTree->Branch(kBranchName, &fWin, "fWin/l");
fCmdTree->Branch(kBranchName, " TRecCmdEvent", &fCmdEvent);
fGuiTree->Branch(kBranchName, "TRecGuiEvent", &fGuiEvent);
fExtraTree->Branch(kBranchName, "TRecExtraEvent", &fExtraEvent);
Int_t numCanvases = gROOT->GetListOfCanvases()->LastIndex();
if (numCanvases >= 0){
TIter nextwindow (gClient->GetListOfWindows());
TGWindow *twin;
Window_t twin2;
Int_t cnt = 0;
while ((twin = (TGWindow*) nextwindow())) {
twin2 = (Window_t) twin->GetId();
if (IsFiltered(twin2)) {
if (gDebug > 0) {
cout << "WindowID "<< twin2 << " filtered" << endl;
}
}
else if (twin != gClient->GetRoot()) {
RegisterWindow(twin2);
}
cnt++;
}
}
fTimer->TurnOn();
Info("TRecorderRecording::StartRecording", "Recording started. Log file: %s", fFile->GetName());
return kTRUE;
}
void TRecorderRecording::Stop(TRecorder *, Bool_t guiCommand)
{
TQObject::Disconnect("TGFrame", "ProcessedConfigure(Event_t*)", this, "RecordGuiCNEvent(Event_t*)");
TQObject::Disconnect("TPad", "RecordPave(const TObject*)", this, "RecordPave(const TObject*)");
TQObject::Disconnect("TPad", "RecordLatex(const TObject*)", this, "RecordText(const TObject*)");
TQObject::Disconnect("TPad", "EventPave()", this, "FilterEventPave()");
TQObject::Disconnect("TPad", "StartEditing()", this, "StartEditing()");
gClient->Disconnect(gClient, "ProcessedEvent(Event_t*, Window_t)", this, "RecordGuiEvent(Event_t*, Window_t)");
gClient->Disconnect(gClient, "RegisteredWindow(Window_t)", this, "RegisterWindow(Window_t)");
gApplication->Disconnect(gApplication, "LineProcessed(const char*)", this, "RecordCmdEvent(const char* line)");
if (fCmdEventPending && guiCommand)
fCmdTree->Fill();
fRecorder->Write("recorder");
fFile->Write();
fFile->Close();
fTimer->TurnOff();
Info("TRecorderRecording::Stop", "Recording finished.");
fRecorder->ChangeState(new TRecorderInactive());
}
void TRecorderRecording::RegisterWindow(Window_t w)
{
fWin = (ULong64_t) w;
fWinTree->Fill();
}
void TRecorderRecording::RecordCmdEvent(const char *line)
{
if (fCmdEventPending)
fCmdTree->Fill();
fCmdEvent->SetTime(fTimer->GetAbsTime());
fCmdEvent->SetText((char*)line);
fCmdEventPending = kTRUE;
return;
}
void TRecorderRecording::RecordGuiEvent(Event_t* e, Window_t wid)
{
if (fFilteredIdsCount && IsFiltered(e->fWindow))
return;
if (fFilterEventPave && (e->fCode == 1)) {
fFilterEventPave = kFALSE;
return;
}
fFilterEventPave = kFALSE;
CopyEvent(e, wid);
fGuiEvent->SetTime(fTimer->GetAbsTime());
fGuiTree->Fill();
}
void TRecorderRecording::RecordGuiCNEvent(Event_t* e)
{
if (fFilteredIdsCount && IsFiltered(e->fWindow))
return;
SetTypeOfConfigureNotify(e);
CopyEvent(e, 0);
fGuiEvent->SetTime(fTimer->GetAbsTime());
fGuiTree->Fill();
}
void TRecorderRecording::RecordPave(const TObject* obj)
{
ULong_t extratime = fBeginPave;
ULong_t interval = (unsigned long)fTimer->GetAbsTime() - fBeginPave;
TPaveLabel *pavel = (TPaveLabel *) obj;
const char* label;
label = pavel->GetLabel();
TString aux = "";
TString cad = "";
cad = "TPaveLabel *p = new TPaveLabel(";
cad += pavel->GetX1();
cad += ",";
cad += pavel->GetY1();
cad += ",";
cad += pavel->GetX2();
cad += ",";
cad += pavel->GetY2();
cad += ",\"\"); p->Draw(); gPad->Modified(); gPad->Update();";
Int_t i, len = (Int_t)strlen(label);
interval /= (ULong_t)(len + 2);
RecordExtraEvent(cad, extratime);
for (i=0; i < len; ++i) {
cad = "p->SetLabel(\"";
cad += (aux += label[i]);
cad += "\"); ";
#ifndef R__WIN32
cad += " p->SetTextFont(83); p->SetTextSizePixels(14); ";
#endif
cad += " gPad->Modified(); gPad->Update();";
extratime += interval;
RecordExtraEvent(cad, extratime);
}
cad = "p->SetTextFont(";
cad += pavel->GetTextFont();
cad += "); p->SetTextSize(";
cad += pavel->GetTextSize();
cad += "); gPad->Modified(); gPad->Update();";
extratime += interval;
RecordExtraEvent(cad, extratime);
}
void TRecorderRecording::RecordText(const TObject* obj)
{
ULong_t extratime = fBeginPave;
ULong_t interval = (unsigned long)fTimer->GetAbsTime() - fBeginPave;
TLatex *texto = (TLatex *) obj;
const char* label;
label = texto->GetTitle();
TString aux = "";
TString cad = "";
cad = "TLatex *l = new TLatex(";
cad += texto->GetX();
cad += ",";
cad += texto->GetY();
cad += ",\"\"); l->Draw(); gPad->Modified(); gPad->Update();";
Int_t i, len = (Int_t)strlen(label);
interval /= (ULong_t)(len + 2);
RecordExtraEvent(cad, extratime);
for (i=0; i < len; ++i) {
cad = "l->SetTitle(\"";
cad += (aux += label[i]);
cad += "\"); ";
#ifndef R__WIN32
cad += " l->SetTextFont(83); l->SetTextSizePixels(14); ";
#endif
cad += " gPad->Modified(); gPad->Update();";
extratime += interval;
RecordExtraEvent(cad, extratime);
}
cad = "l->SetTextFont(";
cad += texto->GetTextFont();
cad += "); l->SetTextSize(";
cad += texto->GetTextSize();
cad += "); gPad->Modified(); gPad->Update();";
cad += " TVirtualPad *spad = gPad->GetCanvas()->GetSelectedPad();";
cad += " gPad->GetCanvas()->Selected(spad, l, kButton1Down);";
extratime += interval;
RecordExtraEvent(cad, extratime);
}
void TRecorderRecording::FilterEventPave()
{
fFilterEventPave = kTRUE;
}
void TRecorderRecording::StartEditing()
{
fBeginPave = (long)fTimer->GetAbsTime();
}
void TRecorderRecording::RecordExtraEvent(TString line, ULong_t ExtTime)
{
fExtraEvent->SetTime(TTime(ExtTime));
fExtraEvent->SetText(line);
fExtraTree->Fill();
}
void TRecorderRecording::CopyEvent(Event_t *e, Window_t wid)
{
fGuiEvent->fType = e->fType;
fGuiEvent->fWindow = e->fWindow;
fGuiEvent->fTime = e->fTime;
fGuiEvent->fX = e->fX;
fGuiEvent->fY = e->fY;
fGuiEvent->fXRoot = e->fXRoot;
fGuiEvent->fYRoot = e->fYRoot;
fGuiEvent->fCode = e->fCode;
fGuiEvent->fState = e->fState;
fGuiEvent->fWidth = e->fWidth;
fGuiEvent->fHeight = e->fHeight;
fGuiEvent->fCount = e->fCount;
fGuiEvent->fSendEvent = e->fSendEvent;
fGuiEvent->fHandle = e->fHandle;
fGuiEvent->fFormat = e->fFormat;
for(Int_t i=0; i<5; ++i)
fGuiEvent->fUser[i] = e->fUser[i];
if (e->fType == kGKeyPress || e->fType == kKeyRelease) {
char tmp[10] = {0};
UInt_t keysym = 0;
gVirtualX->LookupString(e, tmp, sizeof(tmp), keysym);
fGuiEvent->fCode = keysym;
}
fGuiEvent->fMasked = wid;
}
Bool_t TRecorderRecording::IsFiltered(Window_t id)
{
for(Int_t i=0; i < fFilteredIdsCount; ++i)
if (id == fFilteredIds[i])
return kTRUE;
return kFALSE;
}
void TRecorderRecording::SetTypeOfConfigureNotify(Event_t *e)
{
if ((e->fX == 0 && e->fX == 0) || e->fFormat == 32 ) {
e->fUser[4] = TRecGuiEvent::kCNFilter;
return;
}
#ifdef WIN32
e->fUser[4] = TRecGuiEvent::kCNMoveResize;
#else
TGWindow *w = gClient->GetWindowById(e->fWindow);
if (w) {
TGFrame* t = (TGFrame*)w;
if (t->GetWidth() == e->fWidth && t->GetHeight() == e->fHeight &&
e->fX == t->GetX() && e->fY == t->GetY()) {
e->fUser[4] = TRecGuiEvent::kCNFilter;
}
else {
if (t->GetWidth() == e->fWidth && t->GetHeight() == e->fHeight) {
e->fUser[4] = TRecGuiEvent::kCNMove;
}
else {
e->fUser[4] = TRecGuiEvent::kCNResize;
}
}
}
#endif
}
ClassImp(TGRecorder)
TGRecorder::TGRecorder(const TGWindow *p, UInt_t w, UInt_t h) :
TGMainFrame(p ? p : gClient->GetRoot(), w, h)
{
SetCleanup(kDeepCleanup);
fRecorder = new TRecorder();
fFilteredIds[0] = GetId();
TGHorizontalFrame *hframe = new TGHorizontalFrame(this, 200, 75, kChildFrame | kFixedHeight, (Pixel_t)0x000000);
fFilteredIds[1] = hframe->GetId();
TGVerticalFrame *vframe = new TGVerticalFrame(hframe, 200, 75, kChildFrame | kFixedHeight, (Pixel_t)0x000000);
fFilteredIds[2] = vframe->GetId();
TGLabel *fStatusLabel = new TGLabel(vframe, "Status:");
fStatusLabel->SetTextColor(0x7cffff);
fStatusLabel->SetBackgroundColor((Pixel_t)0x000000);
vframe->AddFrame(fStatusLabel, new TGLayoutHints(kLHintsLeft | kLHintsTop,2,2,2,2));
fFilteredIds[3] = fStatusLabel->GetId();
TGLabel *fTimeLabel = new TGLabel(vframe, "Time: ");
fTimeLabel->SetTextColor(0x7cffff);
fTimeLabel->SetBackgroundColor((Pixel_t)0x000000);
vframe->AddFrame(fTimeLabel, new TGLayoutHints(kLHintsLeft | kLHintsTop,2,2,13,2));
fFilteredIds[4] = fTimeLabel->GetId();
hframe->AddFrame(vframe, new TGLayoutHints(kLHintsLeft | kLHintsExpandY));
vframe = new TGVerticalFrame(hframe, 200, 75, kChildFrame | kFixedHeight, (Pixel_t)0x000000);
fFilteredIds[5] = vframe->GetId();
fStatus = new TGLabel(vframe, "Inactive");
fStatus->SetTextColor(0x7cffff);
fStatus->SetBackgroundColor((Pixel_t)0x000000);
vframe->AddFrame(fStatus, new TGLayoutHints(kLHintsLeft | kLHintsTop,2,2,2,2));
fFilteredIds[6] = fStatus->GetId();
fTimeDisplay = new TGLabel(vframe, "00:00:00");
fTimeDisplay->SetTextColor(0x7cffff);
fTimeDisplay->SetTextFont("Helvetica -34", kFALSE);
fTimeDisplay->SetBackgroundColor((Pixel_t)0x000000);
vframe->AddFrame(fTimeDisplay, new TGLayoutHints(kLHintsLeft | kLHintsTop,2,2,2,2));
fFilteredIds[7] = fTimeDisplay->GetId();
hframe->AddFrame(vframe, new TGLayoutHints(kLHintsLeft | kLHintsExpandY,10,0,0,0));
AddFrame(hframe, new TGLayoutHints(kLHintsExpandX,2,2,2,2));
hframe = new TGHorizontalFrame(this, 200, 200);
fFilteredIds[8] = hframe->GetId();
fStartStop = new TGPictureButton(hframe,gClient->GetPicture("record.png"));
fStartStop->Connect("Clicked()","TGRecorder",this,"StartStop()");
hframe->AddFrame(fStartStop, new TGLayoutHints(kLHintsLeft | kLHintsTop,2,2,2,2));
fStartStop->Resize(40,40);
fFilteredIds[9] = fStartStop->GetId();
fReplay = new TGPictureButton(hframe,gClient->GetPicture("replay.png"));
fReplay->Connect("Clicked()","TGRecorder",this,"Replay()");
hframe->AddFrame(fReplay, new TGLayoutHints(kLHintsLeft | kLHintsTop,2,2,2,2));
fReplay->Resize(40,40);
fFilteredIds[10] = fReplay->GetId();
fCursorCheckBox = new TGCheckButton(this,"Show mouse cursor");
AddFrame(fCursorCheckBox, new TGLayoutHints(kLHintsCenterX, 2,2,2,2));
fFilteredIds[11] = fCursorCheckBox->GetId();
fTimer = new TTimer(25);
fTimer->Connect("Timeout()", "TGRecorder", this, "Update()");
AddFrame(hframe, new TGLayoutHints(kLHintsCenterX,2,2,2,2));
SetWindowName("ROOT Event Recorder");
MapSubwindows();
Layout();
MapWindow();
SetDefault();
}
void TGRecorder::SetDefault()
{
fTimeDisplay->SetText("00:00:00");
fReplay->SetPicture(gClient->GetPicture("replay.png"));
fReplay->SetEnabled(kTRUE);
fCursorCheckBox->SetEnabled(kTRUE);
fCursorCheckBox->SetOn(kTRUE);
fStartStop->SetPicture(gClient->GetPicture("record.png"));
fStartStop->SetEnabled(kTRUE);
}
void TGRecorder::Update()
{
struct tm *running;
static int cnt = 0;
TString stime;
time( &fElapsed );
time_t elapsed_time = (time_t)difftime( fElapsed, fStart );
running = gmtime( &elapsed_time );
switch(fRecorder->GetState()) {
case TRecorder::kRecording:
case TRecorder::kReplaying:
if (cnt >= 10) {
if (fRecorder->GetState() == TRecorder::kReplaying)
fStatus->SetText("Replaying");
else
fStatus->SetText("Recording");
stime.Form("%02d:%02d:%02d", running->tm_hour,
running->tm_min, running->tm_sec);
fTimeDisplay->SetText(stime.Data());
cnt = 0;
if (gVirtualX->EventsPending()) {
fStatus->SetText("Waiting...");
fStatus->SetTextColor((Pixel_t)0xff0000);
}
else {
fStatus->SetTextColor((Pixel_t)0x7cffff);
}
fStatus->Resize();
fTimeDisplay->Resize();
}
else
++cnt;
fTimer->Reset();
break;
case TRecorder::kInactive:
fStatus->SetText("Inactive");
fStatus->SetTextColor((Pixel_t)0x7cffff);
fStatus->Resize();
fTimer->TurnOff();
SetDefault();
break;
default:
break;
}
}
void TGRecorder::StartStop()
{
static const char *gFiletypes[] = {"All files", "*", "Text files", "*.txt", "ROOT files", "*.root", 0, 0};
TGFileDialog *filedialog;
TGFileInfo fi;
switch(fRecorder->GetState()) {
case TRecorder::kInactive:
fi.fFileTypes = gFiletypes;
fi.fOverwrite = kFALSE;
filedialog = new TGFileDialog(gClient->GetDefaultRoot(), gClient->GetDefaultRoot(), kFDSave,&fi);
if (fi.fFilename && strlen(fi.fFilename)) {
if (!gROOT->GetListOfCanvases()->IsEmpty()) {
fRecorder->PrevCanvases(fi.fFilename, "RECREATE");
fRecorder->Start(fi.fFilename, "UPDATE", fFilteredIds, fgWidgetsCount);
}
else {
fRecorder->Start(fi.fFilename, "RECREATE", fFilteredIds, fgWidgetsCount);
}
fCursorCheckBox->SetDisabledAndSelected(kTRUE);
fStartStop->SetPicture(gClient->GetPicture("stop.png"));
fReplay->SetEnabled(kFALSE);
fTimer->TurnOn();
time( &fStart );
}
break;
case TRecorder::kRecording:
fRecorder->Stop(kTRUE);
break;
case TRecorder::kReplaying:
fRecorder->Pause();
fStartStop->SetPicture(gClient->GetPicture("replay.png"));
break;
case TRecorder::kPaused:
fRecorder->Resume();
fStartStop->SetPicture(gClient->GetPicture("pause.png"));
break;
default:
break;
}
}
void TGRecorder::Replay()
{
TGFileInfo fi;
TGFileDialog *filedialog;
switch(fRecorder->GetState()) {
case TRecorder::kInactive:
filedialog = new TGFileDialog(gClient->GetDefaultRoot(), gClient->GetDefaultRoot(), kFDOpen, &fi);
if (fi.fFilename && strlen(fi.fFilename)) {
if (fRecorder->Replay(fi.fFilename, fCursorCheckBox->IsOn())) {
fTimer->TurnOn();
time( &fStart );
fReplay->SetPicture(gClient->GetPicture("stop.png"));
fStartStop->SetPicture(gClient->GetPicture("pause.png"));
if (fCursorCheckBox->IsOn())
fStartStop->SetEnabled(kFALSE);
fCursorCheckBox->SetEnabled(kFALSE);
}
}
break;
case TRecorder::kReplaying:
case TRecorder::kPaused:
fRecorder->ReplayStop();
break;
default:
break;
}
}
TGRecorder::~TGRecorder()
{
fTimer->TurnOff();
delete fTimer;
Cleanup();
}
ClassImp(TRecCmdEvent)
ClassImp(TRecGuiEvent)
void TRecGuiEvent::ReplayEvent(Bool_t showMouseCursor)
{
Event_t *e = CreateEvent(this);
if (e->fType == kConfigureNotify) {
TGWindow *w = gClient->GetWindowById(e->fWindow);
if (w) {
if (e->fUser[4] == TRecGuiEvent::kCNMove) {
w->Move(e->fX, e->fY);
}
else {
if (e->fUser[4] == TRecGuiEvent::kCNResize) {
w->Resize(e->fWidth, e->fHeight);
}
else {
if (e->fUser[4] == TRecGuiEvent::kCNMoveResize) {
w->MoveResize(e->fX, e->fY, e->fWidth, e->fHeight);
}
else {
if (gDebug > 0)
Error("TRecGuiEvent::ReplayEvent", "kConfigureNotify: Unknown value: fUser[4] = %d ", e->fUser[4]);
}
}
}
}
else {
if (gDebug > 0)
Error("TRecGuiEvent::ReplayEvent", "kConfigureNotify: Window %x does not exist anymore ");
}
return;
}
if (e->fType == kMotionNotify && showMouseCursor) {
TGWindow *w = gClient->GetWindowById(e->fWindow);
if (w)
gVirtualX->Warp(e->fX, e->fY, w->GetId());
}
if ((e->fType == kEnterNotify || e->fType == kLeaveNotify ) && showMouseCursor) {
TGWindow *w = gClient->GetWindowById(e->fWindow);
if (w)
gVirtualX->Warp(e->fX, e->fY, w->GetId());
}
if (!fMasked)
gClient->HandleEvent(e);
else
gClient->HandleMaskEvent(e, fMasked);
}
Event_t *TRecGuiEvent::CreateEvent(TRecGuiEvent *ge)
{
Event_t *e = new Event_t();
e->fType = ge->fType;
e->fWindow = ge->fWindow;
e->fTime = ge->fTime;
e->fX = ge->fX;
e->fY = ge->fY;
e->fXRoot = ge->fXRoot;
e->fYRoot = ge->fYRoot;
e->fCode = ge->fCode;
e->fState = ge->fState;
e->fWidth = ge->fWidth;
e->fHeight = ge->fHeight;
e->fCount = ge->fCount;
e->fSendEvent = ge->fSendEvent;
e->fHandle = ge->fHandle;
e->fFormat = ge->fFormat;
for(Int_t i=0; i<5; ++i)
e->fUser[i] = ge->fUser[i];
if (ge->fType == kGKeyPress || ge->fType == kKeyRelease) {
e->fCode = gVirtualX->KeysymToKeycode(ge->fCode);
#ifdef R__WIN32
e->fUser[1] = 1;
e->fUser[2] = e->fCode;
#endif
}
return e;
}
ClassImp(TRecWinPair)