#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"
#include "TVirtualDragManager.h"
#include "TGPicture.h"
#include "KeySymbols.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)
class TGCursorWindow : public TGFrame {
protected:
Pixmap_t fPic, fMask;
public:
TGCursorWindow();
virtual ~TGCursorWindow();
};
static TGCursorWindow *gCursorWin = 0;
static Int_t gDecorWidth = 0;
static Int_t gDecorHeight = 0;
TGCursorWindow::TGCursorWindow() :
TGFrame(gClient->GetDefaultRoot(), 32, 32, kTempFrame)
{
SetWindowAttributes_t wattr;
const TGPicture *pbg = fClient->GetPicture("recursor.png");
fPic = pbg->GetPicture();
fMask = pbg->GetMask();
gVirtualX->ShapeCombineMask(fId, 0, 0, fMask);
SetBackgroundPixmap(fPic);
wattr.fMask = kWAOverrideRedirect | kWASaveUnder;
wattr.fSaveUnder = kTRUE;
wattr.fOverrideRedirect = kTRUE;
gVirtualX->ChangeWindowAttributes(fId, &wattr);
}
TGCursorWindow::~TGCursorWindow()
{
}
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 delPreviousState)
{
if (delPreviousState)
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)
{
fCanv = 0;
fCmdTree = 0;
fCmdTreeCounter = 0;
fEventReplayed = kTRUE;
fExtraTree = 0;
fExtraTreeCounter = 0;
fFilterStatusBar = kFALSE;
fGuiTree = 0;
fGuiTreeCounter = 0;
fNextEvent = 0;
fRecorder = 0;
fRegWinCounter = 0;
fShowMouseCursor = kTRUE;
fWaitingForWindow = kFALSE;
fWin = 0;
fWinTree = 0;
fWinTreeEntries = 0;
fFile = TFile::Open(filename);
fCmdEvent = new TRecCmdEvent();
fGuiEvent = new TRecGuiEvent();
fExtraEvent = new TRecExtraEvent();
fWindowList = new TList();
fTimer = new TTimer();
fMutex = new TMutex(kFALSE);
if (!gCursorWin)
gCursorWin = new TGCursorWindow();
}
TRecorderReplaying::~TRecorderReplaying()
{
fTimer->Disconnect(fTimer, "Timeout()", this, "ReplayRealtime()");
fTimer->TurnOff();
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;
if (gCursorWin)
gCursorWin->DeleteWindow();
gCursorWin = 0;
}
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());
if (f && !f->IsZombie()) {
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)) {
std::ios::fmtflags f = std::cout.flags();
std::cout << " Window registered: new ID: " << std::hex << w <<
" previous ID: " << fWin << std::dec << std::endl;
std::cout.flags( f );
}
fMutex->Lock();
fRegWinCounter++;
TRecWinPair *ids = new TRecWinPair(fWin, w);
fWindowList->Add(ids);
if (fWaitingForWindow && fGuiEvent->fWindow == fWin) {
if (gDebug > 0) {
std::ios::fmtflags f = std::cout.flags();
std::cout << " Window " << std::hex << fGuiEvent->fWindow <<
" registered." << std::dec << std::endl;
std::cout.flags( f );
}
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 (!found && fGuiEvent->fWindow == 0) {
fGuiEvent->fWindow = gVirtualX->GetDefaultRootWindow();
found = kTRUE;
}
else if (!found && 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 && fGuiEvent->fWindow == 0) {
fGuiEvent->fWindow = gVirtualX->GetDefaultRootWindow();
found = kTRUE;
}
if (found) {
fMutex->UnLock();
return kTRUE;
}
if (gDebug > 0) {
std::ios_base::fmtflags org_flags = std::cout.flags();
std::cout << "fGuiTreeCounter = " << std::dec << fGuiTreeCounter <<
" No mapping found for ID " << std::hex << fGuiEvent->fWindow << std::endl;
TRecorderInactive::DumpRootEvent(fGuiEvent,0);
std::cout.flags(org_flags);
}
fTimer->Stop();
fWaitingForWindow = kTRUE;
fMutex->UnLock();
return kFALSE;
}
Bool_t TRecorderReplaying::FilterEvent(TRecGuiEvent *e)
{
if (e->fType == kClientMessage) {
if ((e->fFormat == 32) && (e->fHandle != TRecGuiEvent::kROOT_MESSAGE)
&& ((Atom_t)e->fUser[0] == TRecGuiEvent::kWM_DELETE_WINDOW))
return kFALSE;
else
return kTRUE;
}
if (e->fType == kConfigureNotify && e->fUser[4] == TRecGuiEvent::kCNFilter) {
return kTRUE;
}
if (e->fType == kOtherEvent) {
if (e->fFormat >= kGKeyPress && e->fFormat < kOtherEvent)
return kFALSE;
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 (!fGuiEvent || !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::kGuiEvent)
return kFALSE;
if (gDebug > 0) {
std::cout << "Event overlapping " <<
kRecEventNames[((TRecGuiEvent*)fNextEvent)->fType] << std::endl;
TRecorderInactive::DumpRootEvent(((TRecGuiEvent*)fNextEvent), 0);
}
TRecGuiEvent *e = (TRecGuiEvent*) fNextEvent;
if (e->fType == kButtonPress || e->fType == kButtonRelease ||
e->fType == kMotionNotify)
return kTRUE;
return kFALSE;
}
void TRecorderReplaying::ReplayRealtime()
{
UInt_t keysym;
char str[2];
if ((gROOT->GetEditorMode() == kText) ||
(gROOT->GetEditorMode() == kPaveLabel)){
gROOT->SetEditorMode();
}
if (gVirtualX->EventsPending()) {
gSystem->ProcessEvents();
return;
}
if (!fEventReplayed && !CanOverlap())
return;
if (fNextEvent) {
fEventReplayed = 0;
fPreviousEventTime = fNextEvent->GetTime();
if (fNextEvent->GetType() == TRecEvent::kGuiEvent) {
TRecGuiEvent *ev = (TRecGuiEvent *)fNextEvent;
if (ev->fType == kGKeyPress && ev->fState & kKeyControlMask) {
Event_t *e = ev->CreateEvent(ev);
gVirtualX->LookupString(e, str, sizeof(str), keysym);
if ((keysym & ~0x20) == kKey_S) {
fEventReplayed = 1;
PrepareNextEvent();
ev->ReplayEvent(fShowMouseCursor);
return;
}
}
}
fNextEvent->ReplayEvent(fShowMouseCursor);
fEventReplayed = 1;
}
if (!PrepareNextEvent()) {
Info("TRecorderReplaying::ReplayRealtime", "Replaying finished");
fRecorder->ChangeState(new TRecorderInactive());
return;
}
else {
if (fNextEvent)
fTimer->Start(Long_t(fNextEvent->GetTime() - fPreviousEventTime));
}
}
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(Long_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) return;
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);
std::cout << "[" << i << "] " << "fTime=" <<
(ULong64_t) fCmdEvent->GetTime() << " fText=" <<
fCmdEvent->GetText() << std::endl;
}
std::cout << std::endl;
delete fCmdEvent;
delete file;
}
void TRecorderInactive::ListGui(const char *filename)
{
TFile *file = TFile::Open(filename);
if (!file) return;
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)
{
std::ios::fmtflags f = std::cout.flags();
std::cout << "[" << n << "] " << std::dec << std::setw(10)
<< e->GetTime().AsString() << std::setw(15) << kRecEventNames[e->fType]
<< " fW:" << std::hex << e->fWindow
<< " t:" << std::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)
std::cout << "[" << i << "]=" << DisplayValid(e->fUser[i]);
if (e->fMasked)
std::cout << " | fM:" << std::hex << e->fMasked;
std::cout << std::endl;
std::cout.flags( f );
}
void TRecorderInactive::PrevCanvases(const char *filename, Option_t *option)
{
fCollect = gROOT->GetListOfCanvases();
TFile *f = TFile::Open(filename, option);
if (f && !f->IsZombie()) {
fCollect->Write();
f->Close();
delete f;
}
}
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);
fMouseTimer = new TTimer(50, kTRUE);
fMouseTimer->Connect("Timeout()", "TRecorderRecording", this,
"RecordMousePosition()");
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 fMouseTimer;
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*)");
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()");
TQObject::Connect("TGuiBldDragManager", "TimerEvent(Event_t*)",
"TRecorderRecording", this, "RecordGuiBldEvent(Event_t*)");
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) {
std::cout << "WindowID "<< twin2 << " filtered" << std::endl;
}
}
else if (twin != gClient->GetRoot()) {
RegisterWindow(twin2);
}
cnt++;
}
}
fTimer->TurnOn();
fMouseTimer->Start(50);
Info("TRecorderRecording::StartRecording", "Recording started. Log file: %s",
fFile->GetName());
return kTRUE;
}
void TRecorderRecording::Stop(TRecorder *, Bool_t guiCommand)
{
TQObject::Disconnect("TGuiBldDragManager", "TimerEvent(Event_t*)", this,
"RecordGuiBldEvent(Event_t*)");
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*)");
if (fCmdEventPending && guiCommand)
fCmdTree->Fill();
fRecorder->Write("recorder");
fFile->Write();
fFile->Close();
fTimer->TurnOff();
fMouseTimer->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;
if (e->fType == kSelectionClear || e->fType == kSelectionRequest ||
e->fType == kSelectionNotify)
return;
CopyEvent(e, wid);
fGuiEvent->SetTime(fTimer->GetAbsTime());
fGuiTree->Fill();
}
void TRecorderRecording::RecordGuiBldEvent(Event_t *e)
{
e->fFormat = e->fType;
e->fType = kOtherEvent;
CopyEvent(e, 0);
fGuiEvent->SetTime(fTimer->GetAbsTime());
fGuiTree->Fill();
}
void TRecorderRecording::RecordMousePosition()
{
Window_t dum;
Event_t ev;
ev.fCode = 0;
ev.fType = kMotionNotify;
ev.fState = 0;
ev.fWindow = 0;
ev.fUser[0] = ev.fUser[1] = ev.fUser[2] = ev.fUser[3] = ev.fUser[4] = 0;
ev.fCount = 0;
ev.fFormat = 0;
ev.fHandle = 0;
ev.fHeight = 0;
ev.fSendEvent = 0;
ev.fTime = 0;
ev.fWidth = 0;
gVirtualX->QueryPointer(gVirtualX->GetDefaultRootWindow(), dum, dum,
ev.fXRoot, ev.fYRoot, ev.fX, ev.fY, ev.fState);
ev.fXRoot -= gDecorWidth;
ev.fYRoot -= gDecorHeight;
RecordGuiEvent(&ev, 0);
fMouseTimer->Reset();
}
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)
{
Long64_t extratime = fBeginPave;
Long64_t interval = (Long64_t)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 /= (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)
{
Long64_t extratime = fBeginPave;
Long64_t interval = (Long64_t)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 /= (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 = fTimer->GetAbsTime();
}
void TRecorderRecording::RecordExtraEvent(TString line, TTime extTime)
{
fExtraEvent->SetTime(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;
if (fGuiEvent->fHandle == gROOT_MESSAGE)
fGuiEvent->fHandle = TRecGuiEvent::kROOT_MESSAGE;
for(Int_t i=0; i<5; ++i)
fGuiEvent->fUser[i] = e->fUser[i];
if (fGuiEvent->fUser[0] == (Int_t)gWM_DELETE_WINDOW)
fGuiEvent->fUser[0] = TRecGuiEvent::kWM_DELETE_WINDOW;
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->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)
{
TGHorizontalFrame *hframe;
TGVerticalFrame *vframe;
SetCleanup(kDeepCleanup);
fRecorder = new TRecorder();
fFilteredIds[0] = GetId();
hframe = new TGHorizontalFrame(this, 200, 75, kChildFrame | kFixedHeight,
(Pixel_t)0x000000);
fFilteredIds[1] = hframe->GetId();
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->SetStyle(gClient->GetStyle());
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->SetStyle(gClient->GetStyle());
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));
SetEditDisabled(kEditDisable | kEditDisableGrab);
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
};
TGFileInfo fi;
switch(fRecorder->GetState()) {
case TRecorder::kInactive:
fi.fFileTypes = gFiletypes;
fi.fOverwrite = kFALSE;
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;
switch(fRecorder->GetState()) {
case TRecorder::kInactive:
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)
{
Int_t px, py, dx, dy;
Window_t wtarget;
Event_t *e = CreateEvent(this);
if (e->fType == kSelectionClear || e->fType == kSelectionRequest ||
e->fType == kSelectionNotify) {
delete e;
return;
}
if (e->fType == kConfigureNotify) {
TGWindow *w = gClient->GetWindowById(e->fWindow);
if (w) {
WindowAttributes_t attr;
if (e->fUser[4] == TRecGuiEvent::kCNMove) {
gVirtualX->GetWindowAttributes(e->fWindow, attr);
if ((e->fX - attr.fX > 0) && (e->fY - attr.fY > 0))
w->Move(e->fX - attr.fX, e->fY - attr.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] = %ld ",
e->fUser[4]);
}
}
}
}
else {
if (gDebug > 0)
Error("TRecGuiEvent::ReplayEvent",
"kConfigureNotify: Window does not exist anymore ");
}
delete e;
return;
}
if (showMouseCursor && e->fType == kButtonPress) {
gVirtualX->TranslateCoordinates(e->fWindow, gVirtualX->GetDefaultRootWindow(),
e->fX, e->fY, px, py, wtarget);
dx = px - gCursorWin->GetX();
dy = py - gCursorWin->GetY();
if (TMath::Abs(dx) > 5) gDecorWidth += dx;
if (TMath::Abs(dy) > 5) gDecorHeight += dy;
}
if (showMouseCursor && e->fType == kMotionNotify) {
if (gCursorWin && e->fWindow == gVirtualX->GetDefaultRootWindow()) {
if (!gCursorWin->IsMapped()) {
gCursorWin->MapRaised();
}
if (gVirtualX->GetDrawMode() == TVirtualX::kCopy) {
gCursorWin->RaiseWindow();
gCursorWin->Move(e->fXRoot + gDecorWidth, e->fYRoot + gDecorHeight);
}
}
}
if (e->fType == kOtherEvent && e->fFormat >= kGKeyPress &&
e->fFormat < kOtherEvent) {
e->fType = (EGEventType)e->fFormat;
if (gDragManager)
gDragManager->HandleTimerEvent(e, 0);
delete e;
return;
}
else {
if (!fMasked)
gClient->HandleEvent(e);
else
gClient->HandleMaskEvent(e, fMasked);
}
delete e;
}
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;
if (e->fHandle == TRecGuiEvent::kROOT_MESSAGE)
e->fHandle = gROOT_MESSAGE;
for(Int_t i=0; i<5; ++i)
e->fUser[i] = ge->fUser[i];
if (e->fUser[0] == TRecGuiEvent::kWM_DELETE_WINDOW)
e->fUser[0] = gWM_DELETE_WINDOW;
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)