/* ======================================================================== *\
!
! *
! * This file is part of MARS, the MAGIC Analysis and Reconstruction
! * Software. It is distributed to you in the hope that it can be a useful
! * and timesaving tool in analysing Data of imaging Cerenkov telescopes.
! * It is distributed WITHOUT ANY WARRANTY.
! *
! * Permission to use, copy, modify and distribute this software and its
! * documentation for any purpose is hereby granted without fee,
! * provided that the above copyright notice appear in all copies and
! * that both that copyright notice and this permission notice appear
! * in supporting documentation. It is provided "as is" without express
! * or implied warranty.
! *
!
!
!   Author(s): Thomas Bretz, 4/2003 <mailto:tbretz@astro-uni-wuerzburg.de>
!
!   Copyright: MAGIC Software Development, 2003
!
!
\* ======================================================================== */

/////////////////////////////////////////////////////////////////////////////
//
// MStatusDisplay
//
// This status display can be used (and is used) to display results in
// a tabbed window. The window can be written to and read from a root file
// (see Read and Write) or printed as a postscript file (see SaveAsPS).
//
// It has also to half status lines which can be used to display the status
// or something going on. Together with the status lines it has a progress
// bar which can display the progress of a job or loop.
// Access the progress bar by GetProgressBar()
//
// To add a new tab and get a pointer to the newly created TCanvas
// use AddTab.
//
// If you have a MStatusDisplay and you are not sure whether it was
// destroyed by the user meanwhile use:
//   gROOT->GetListOfSpecials()->FindObject(pointer);
// Each MStatusDisplay is added to list list by its constructor and
// removed from the list by the destructor.
//
// You can redirect an output to a MLog-logstream by calling SetLogStream().
// To disable redirction call SetLogStream(NULL)
//
// Because updates to the tabs are only done/displayed if a tab is active
// using the gui doesn't make things slower (<1%) if the first (legend
// tab) is displayed. This gives you the possibility to look into
// the current progress of a loop without loosing more time than the
// single update of the tab.
//
/////////////////////////////////////////////////////////////////////////////
#include "MStatusDisplay.h"

#include <iostream.h>

#include <TLine.h>                // TLine
#include <TText.h>                // TText
#include <TFile.h>                // gFile
#include <TFrame.h>               // TFrame
#include <TStyle.h>               // gStyle
#include <TCanvas.h>              // TCanvas
#include <TSystem.h>              // gSystem
#include <TDatime.h>              // TDatime
#include <TRandom.h>              // TRandom
#include <TObjArray.h>            // TObjArray
#include <TPostScript.h>          // TPostScript

#include <TGTab.h>                // TGTab
#include <TGLabel.h>              // TGLabel
#include <TG3DLine.h>             // TGHorizontal3DLine
#include <TGButton.h>             // TGPictureButton
#include <TGListBox.h>            // TGListBox
#include <TGProgressBar.h>        // TGHProgressBar

#include <TRootEmbeddedCanvas.h>  // TRootEmbeddedCanvas

#include "MLog.h"                 // MLog
#include "MLogManip.h"            // inf, warn, err

#include "MGList.h"               // MGList
#include "MGMenu.h"               // MGMenu, TGMenu
#include "MParContainer.h"        // MParContainer::GetDescriptor

ClassImp(MStatusDisplay);

// --------------------------------------------------------------------------
//
// Add menu bar to the GUI
//
void MStatusDisplay::AddMenuBar()
{
    //
    // File Menu
    //
    MGPopupMenu *filemenu = new MGPopupMenu(gClient->GetRoot());
    // filemenu->AddEntry("S&ave [F2]", kFileSave);
    // filemenu->AddEntry("Save &As... [Shift-F2]", kFileSaveAs);
    filemenu->AddEntry("Save As status.&ps", kFileSaveAsPS);
    // filemenu->AddEntry("Save As status.&gif", kFileSaveAsGIF);
    // filemenu->AddEntry("Save As status.&C", kFileSaveAsC);
    filemenu->AddEntry("Save As status.&root", kFileSaveAsRoot);
    filemenu->AddSeparator();
    filemenu->AddEntry("Print with &lpr", kFilePrint);
    filemenu->AddEntry("Set printer &name", kFilePrinterName);
    filemenu->AddSeparator();
    filemenu->AddEntry("E&xit", kFileExit);
    filemenu->Associate(this);

    //
    // Tab Menu
    //
    MGPopupMenu *tabmenu = new MGPopupMenu(gClient->GetRoot());
    // tabmenu->AddEntry("S&ave [F2]", kFileSave);
    // tabmenu->AddEntry("Save &As... [Shift-F2]", kFileSaveAs);
    tabmenu->AddEntry("Save As tab-i.&ps", kTabSaveAsPS);
    // tabmenu->AddEntry("Save As tab-i.&gif", kTabSaveAsGIF);
    // tabmenu->AddEntry("Save As tab-i.&C", kTabSaveAsC);
    tabmenu->AddEntry("Save As tab-i.&root", kTabSaveAsRoot);
    tabmenu->AddSeparator();
    tabmenu->AddEntry("Print with &lpr", kFilePrint);
    tabmenu->AddSeparator();
    tabmenu->AddEntry("Next [&+]",     kTabNext);
    tabmenu->AddEntry("Previous [&-]", kTabPrevious);
    tabmenu->Associate(this);

    //
    // Loop Menu
    //
    MGPopupMenu *loopmenu = new MGPopupMenu(gClient->GetRoot());
    loopmenu->AddEntry("&Stop", kLoopStop);
    loopmenu->Associate(this);

    //
    // Menu Bar
    //
    MGMenuBar *menubar = new MGMenuBar(this, 1, 1, kHorizontalFrame);
    menubar->AddPopup("&File", filemenu, NULL);
    menubar->AddPopup("&Tab",  tabmenu,  NULL);
    menubar->AddPopup("&Loop", loopmenu, NULL);
    menubar->BindKeys(this);
    AddFrame(menubar);

    //
    // Line below menu bar
    //
    TGLayoutHints *laylinesep  = new TGLayoutHints(kLHintsTop|kLHintsExpandX);
    fList->Add(laylinesep);

    TGHorizontal3DLine *linesep = new TGHorizontal3DLine(this);
    AddFrame(linesep, laylinesep);

    //
    // Add everything to autodel list
    //
    fList->Add(filemenu);
    fList->Add(loopmenu);
    fList->Add(menubar);
    fList->Add(tabmenu);
    fList->Add(linesep);
}

// --------------------------------------------------------------------------
//
// Add the title tab
//
void MStatusDisplay::AddMarsTab()
{
    // Create Tab1
    TGCompositeFrame *f = fTab->AddTab("-=MARS=-");

    // Add MARS version
    TString txt = "Official Release: V";
    TGLabel *l = new TGLabel(f, txt+MARSVER);
    fList->Add(l);

    TGLayoutHints *layb = new TGLayoutHints(kLHintsCenterX|kLHintsTop, 10, 10, 10, 10);
    fList->Add(layb);
    f->AddFrame(l, layb);

    // Add root version
    txt = "Using ROOT v";
    l = new TGLabel(f, txt+ROOTVER);
    fList->Add(l);

    TGLayoutHints *lay = new TGLayoutHints(kLHintsCenterX|kLHintsTop);
    fList->Add(lay);
    f->AddFrame(l, lay);

    // Add Mars logo picture
    const TGPicture *pic2 = fList->GetPicture("marslogo.xpm");
    if (pic2)
    {
        TGPictureButton *mars  = new TGPictureButton(f, pic2, kPicMars);
        fList->Add(mars);
        mars->Associate(this);

        TGLayoutHints *lay2 = new TGLayoutHints(kLHintsCenterX|kLHintsCenterY, 10, 10, 10, 10);
        fList->Add(lay2);
        f->AddFrame(mars, lay2);
    }

    // Add date and time
    TDatime d;
    l = new TGLabel(f, d.AsString());
    fList->Add(l);
    f->AddFrame(l, lay);

    // Add copyright notice
    l = new TGLabel(f, "(c) MAGIC Software Development, 2000-2003");
    fList->Add(l);
    f->AddFrame(l, layb);
}

// --------------------------------------------------------------------------
//
// Adds the logbook tab to the GUI if it was not added previously.
//
// The logbook is updated four times a second only if the tab is visible.
//
// You can redirect an output to a MLog-logstream by calling SetLogStream().
// To disable redirction call SetLogStream(NULL)
//
// if enable==kFALSE the stdout is disabled/enabled. Otherwise stdout
// is ignored.
//
void MStatusDisplay::SetLogStream(MLog *log, Bool_t enable)
{
    if (log && fLogBox==NULL)
    {
        fLogIdx = fTab->GetNumberOfTabs();

        // Create Tab1
        TGCompositeFrame *f = fTab->AddTab("-Logbook-");

        // Create TGListBox for logging contents
        fLogBox = new TGListBox(f, 1, 1);
        fLogBox->ChangeBackground(TGFrame::GetBlackPixel());

        // Add List box to the tab
        TGLayoutHints *lay = new TGLayoutHints(kLHintsNormal|kLHintsExpandX|kLHintsExpandY);//, 5, 6, 5);
        f->AddFrame(fLogBox, lay);

        // layout and map tab
        Layout();
        MapSubwindows();

        // make it visible
        gClient->ProcessEventsFor(fTab);
    }

    if (log)
    {
        fLog = log;

        log->SetOutputGui(fLogBox, kTRUE);
        log->EnableOutputDevice(MLog::eGui);
        if (!enable)
            log->DisableOutputDevice(MLog::eStdout);

        fLogTimer.Start();
    }
    else
    {
        fLogTimer.Stop();

        fLog->DisableOutputDevice(MLog::eGui);
        fLog->SetOutputGui(NULL);
        if (!enable)
            fLog->EnableOutputDevice(MLog::eStdout);

        fLog = &gLog;
    }
}

// --------------------------------------------------------------------------
//
// Add the Tabs and the predifined Tabs to the GUI
//
void MStatusDisplay::AddTabs()
{
    fTab = new TGTab(this, 300, 300);

    AddMarsTab();

    // Add fTab to Frame
    TGLayoutHints *laytabs = new TGLayoutHints(kLHintsNormal|kLHintsExpandX|kLHintsExpandY, 5, 6, 5);
    AddFrame(fTab, laytabs);

    fList->Add(fTab);
    fList->Add(laytabs);
}

// --------------------------------------------------------------------------
//
// Add the progress bar to the GUI
//
void MStatusDisplay::AddProgressBar()
{
    TGLayoutHints *laybar=new TGLayoutHints(kLHintsExpandX, 5, 6, 5, 5);
    fList->Add(laybar);

    fBar=new TGHProgressBar(this);
    fBar->ShowPosition();
    AddFrame(fBar, laybar);
    fList->Add(fBar);
}


// --------------------------------------------------------------------------
//
// Adds the status lines to the GUI
//
void MStatusDisplay::AddStatusLines()
{
    TGHorizontalFrame *hf = new TGHorizontalFrame(this, 1, 1);

    TGCompositeFrame *f = new TGCompositeFrame(hf, 1, 1, kSunkenFrame);

    fLine1 = new TGLabel(f, "");

    TGLayoutHints *lay = new TGLayoutHints(kLHintsNormal|kLHintsExpandX, 0, 5);
    f->AddFrame(fLine1, lay);
    hf->AddFrame(f, lay);

    fList->Add(f);
    fList->Add(fLine1);
    fList->Add(lay);

    f = new TGCompositeFrame(hf, 1, 1, kSunkenFrame);

    fLine2 = new TGLabel(f, "");
    f->AddFrame(fLine2, lay);
    hf->AddFrame(f, lay);

    TGLayoutHints *layf = new TGLayoutHints(kLHintsNormal|kLHintsExpandX, 5, 0, 0, 3);
    AddFrame(hf, layf);

    fList->Add(layf);
    fList->Add(f);
    fList->Add(fLine2);
    fList->Add(hf);
}

// --------------------------------------------------------------------------
//
// Change the text in the status line 1
//
void MStatusDisplay::SetStatusLine1(const char *txt)
{
    fLine1->SetText(txt);
    gClient->ProcessEventsFor(fLine1);
}

// --------------------------------------------------------------------------
//
// Change the text in the status line 2
//
void MStatusDisplay::SetStatusLine2(const char *txt)
{
    fLine2->SetText(txt);
    gClient->ProcessEventsFor(fLine2);
}

// --------------------------------------------------------------------------
//
// Display information about the name of a container
//
void MStatusDisplay::SetStatusLine2(const MParContainer &cont)
{
    TString txt = cont.GetDescriptor();
    txt += ": ";
    txt += cont.GetTitle();

    SetStatusLine2(txt);
}

// --------------------------------------------------------------------------
//
// Default constructor. Opens a window with a progress bar. Get a pointer
// to the bar by calling GetBar. This pointer can be used for the
// eventloop.
//
// Be carefull: killing or closing the window while the progress meter
//   is still in use may cause segmentation faults. Please kill the window
//   always by deleting the corresponding object.
//
// Update time default: 10s
//
MStatusDisplay::MStatusDisplay(Long_t t)
: TGMainFrame(gClient->GetRoot(), 1, 1), fTimer(this, t, kTRUE), fLog(&gLog), fLogIdx(-1), fLogTimer(this, 250, kTRUE), fLogBox(NULL)
{
    gROOT->GetListOfSpecials()->Add(this);

    //
    // Create a list handling GUI widgets
    //
    fList = new MGList;
    fList->SetOwner();

    //
    // set the smallest and biggest size of the Main frame
    // and move it to its appearance position
    SetWMSizeHints(640, 548, 1280, 1024, 10, 10);
    Move(rand()%100+50, rand()%100+50);

    //
    // Create the layout hint for the root embedded canavses
    //
    fLayCanvas = new TGLayoutHints(kLHintsExpandX|kLHintsExpandY);
    fList->Add(fLayCanvas);

    //
    // Add Widgets (from top to bottom)
    //
    AddMenuBar();
    AddTabs();
    AddProgressBar();
    AddStatusLines();

    //
    // Now do an automatic layout of the widgets and display the window
    //
    Layout();
    MapSubwindows();

    SetWindowName("Status Display");
    SetIconName("Status Display");

    MapWindow();

    //lient->ProcessEventsFor(this);
    gSystem->ProcessEvents();

    //TSeqCollection   *GetListOfCleanups() const   {return fCleanups;}

}

// --------------------------------------------------------------------------
//
// Destruct the window with all its tiles. Also the Progress Bar object
// is deleted.
//
MStatusDisplay::~MStatusDisplay()
{
    SetLogStream(NULL);

    delete fList;

    gROOT->GetListOfSpecials()->Remove(this);
} 

// --------------------------------------------------------------------------
//
// Takes a TGCompositeFrame as argument. Searches for the first
// TRootEmbeddedCanvas which is contained by it and returns a pointer
// to the corresponding TCanvas. If it isn't found NULL is returned.
//
TCanvas *MStatusDisplay::GetCanvas(TGCompositeFrame *cf) const
{
    TIter Next(cf->GetList());

    TGFrameElement *f;
    while ((f=(TGFrameElement*)Next()))
        if (f->fFrame->InheritsFrom(TRootEmbeddedCanvas::Class()))
            return ((TRootEmbeddedCanvas*)f->fFrame)->GetCanvas();

    return NULL;
}

// --------------------------------------------------------------------------
//
// Returns GetCanvas of the i-th Tab.
//
TCanvas *MStatusDisplay::GetCanvas(int i) const
{
    if (i<0 || i>=fTab->GetNumberOfTabs())
    {
        *fLog << warn << "MStatusDisplay::GetCanvas: Out of range." << endl;
        return NULL;
    }

    return GetCanvas(fTab->GetTabContainer(i));
}

// --------------------------------------------------------------------------
//
// Searches for a TRootEmbeddedCanvas in the TGCompositeFramme of the
// Tab with the name 'name'. Returns the corresponding TCanvas or
// NULL if something isn't found.
//
TCanvas *MStatusDisplay::GetCanvas(const TString &name) const
{
    TGFrameElement *f;
    TIter Next(fTab->GetList());
    while ((f=(TGFrameElement*)Next()))
    {
        TObject *frame = f->fFrame;
        if (!frame->InheritsFrom(TGTabElement::Class()))
            continue;

        TGTabElement *tab = (TGTabElement*)frame;
        if (tab->GetString()==name)
            break;
    }

    // Search for the next TGCompositeFrame in the list
    while ((f=(TGFrameElement*)Next()))
    {
        TObject *frame = f->fFrame;
        if (frame->InheritsFrom(TGCompositeFrame::Class()))
            return GetCanvas((TGCompositeFrame*)frame);
    }

    return NULL;
}

// --------------------------------------------------------------------------
//
// Calls TCanvas::cd(), for the canvas returned by GetCanvas.
//
Bool_t MStatusDisplay::CdCanvas(const TString &name)
{
    TCanvas *c = GetCanvas(name);
    if (!c)
        return kFALSE;

    c->cd();
    return kTRUE;
}

// --------------------------------------------------------------------------
//
// Adds a new tab with the name 'name'. Adds a TRootEmbeddedCanvas to the
// tab and returns a reference to the corresponding TCanvas.
//
TCanvas &MStatusDisplay::AddTab(const char *name)
{
    // Add new tab
    TGCompositeFrame *f = fTab->AddTab(name);

    // create root embedded canvas and add it to the tab
    TRootEmbeddedCanvas *ec = new TRootEmbeddedCanvas(name, f, f->GetWidth(), f->GetHeight(), 0);
    f->AddFrame(ec, fLayCanvas);
    fList->Add(ec);

    // set background and border mode of the canvas
    TCanvas &c = *ec->GetCanvas();

    c.SetFillColor(16/*165*//*17*//*203*/);
    c.SetBorderMode(0);

    // If kNoContaxtMenu set set kNoCOntextMenu of the canvas
    if (TestBit(kNoContextMenu))
        c.SetBit(kNoContextMenu);

    // layout and map new tab
#if ROOT_VERSION_CODE < ROOT_VERSION(3,03,00)
    MapSubwindows();
    Layout();
#else
    Layout();
    MapSubwindows();
#endif

    // display new tab in the main frame
    gClient->ProcessEventsFor(fTab);

    *fLog << inf << "Adding Tab '" << name << "' (" << f->GetWidth() << "x";
    *fLog << f->GetHeight() << ", TCanvas=" << &c << ")" << endl;

    // return pointer to new canvas
    return c;
}

// --------------------------------------------------------------------------
//
// Update a canvas in a tab, takes the corresponding TGCompositeFrame
// as an argument
//
void MStatusDisplay::UpdateTab(TGCompositeFrame *f)
{
    if (!f)
        return;

    TCanvas *c=GetCanvas(f);
    if (!c)
        return;

    c->Modified();
    c->Update();
    c->Paint();
}

// --------------------------------------------------------------------------
//
// Saves the given canvas (pad) or all pads (num<0) as a temporary
// postscript file and prints it using 'lpr'. If a printer name is set
// via SetPrinter 'lpr -Pname' is used.
//
Int_t MStatusDisplay::PrintToLpr(Int_t num) const
{
    TString name = "mars";

    for (int i=0; i<6; i++)
        name += (char)(gRandom->Uniform(25)+65);

    name += ".ps";

    const Int_t pages = SaveAsPS(num, name);
    if (!pages)
    {
        *fLog << warn << "MStatusDisplay::PrintToLpr: Sorry, couldn't save file as temporary postscript!" << endl;
        return 0;
    }

    TString cmd="lpr ";
    if (!fPrinter.IsNull())
    {
        cmd += "-P";
        cmd += fPrinter;
        cmd += " ";
    }
    cmd += name;

    gSystem->Exec(cmd);
    gSystem->Unlink(name);

    return pages;
}

// --------------------------------------------------------------------------
//
// Process the kC_COMMAND, kCM_MENU  messages
//
Bool_t MStatusDisplay::ProcessMessageCommandMenu(Long_t id)
{
    switch (id)
    {
    case kLoopStop:
    case kFileExit:
        if (id==kFileExit && !TestBit(kIsLocked))
            delete this;
        fStatus = (Status_t)id;
        return kTRUE;
/*
    case kFileSave:
        cout << "Save..." << endl;
        return kTRUE;

    case kFileSaveAs:
        cout << "SaveAs..." << endl;
        return kTRUE;
*/
    case kFileSaveAsPS:
        //cout << "FileSaveAsPS..." << endl;
        SaveAsPS();
        return kTRUE;
/*
    case kFileSaveAsGIF:
        cout << "FileSaveAsGIF..." << endl;
        SaveAsGIF();
        return kTRUE;

    case kFileSaveAsC:
        cout << "FileSaveAsC..." << endl;
        SaveAsC();
        return kTRUE;
*/
    case kFileSaveAsRoot:
        SaveAsRoot();
        return kTRUE;

    case kFilePrint:
        PrintToLpr();
        return kTRUE;

    case kTabSaveAsPS:
        SaveAsPS(fTab->GetCurrent());
        return kTRUE;
/*
    case kTabSaveAsGIF:
        cout << "TabSaveAsGIF... " << fTab->GetCurrent() <<  endl;
        SaveAsGIF(fTab->GetCurrent());
        return kTRUE;

    case kTabSaveAsC:
        cout << "TabSaveAsC... " << fTab->GetCurrent() <<  endl;
        SaveAsC(fTab->GetCurrent());
        return kTRUE;
*/
    case kTabSaveAsRoot:
        SaveAsRoot(fTab->GetCurrent());
        return kTRUE;

    case kTabPrint:
        PrintToLpr(fTab->GetCurrent());
        return kTRUE;

    case kTabNext:
        fTab->SetTab(fTab->GetCurrent()+1);
        return kTRUE;

    case kTabPrevious:
        fTab->SetTab(fTab->GetCurrent()-1);
        return kTRUE;

    default:
        cout << "Command-Menu: Id=" << id << endl;
    }
    return kTRUE;

}

// --------------------------------------------------------------------------
//
// Process the kC_COMMAND messages
//
Bool_t MStatusDisplay::ProcessMessageCommand(Long_t submsg, Long_t mp1, Long_t mp2)
{
    switch (submsg)
    {
    case kCM_MENU:
        return ProcessMessageCommandMenu(mp1);

    case kCM_MENUSELECT:
        cout << "Menuselect #" << mp1 << endl;
        return kTRUE;

    case kCM_TAB:
        for (int i=1; i<fTab->GetNumberOfTabs(); i++)
            fTab->GetTabContainer(i)->UnmapWindow();
        UpdateTab(fTab->GetTabContainer(mp1));
        fTab->GetTabContainer(mp1)->MapWindow();
        /*
        if (mp1>0)
            fMenu->AddPopup("&CaOs", fCaOs, NULL);
        else
            fMenu->RemovePopup("CaOs");
        fMenu->Resize(fMenu->GetDefaultSize());
        MapSubwindows();
        MapWindow();
        */
        return kTRUE;

    case kCM_BUTTON:
        if (mp1==kPicMars)
            return kTRUE;
        return kTRUE;
    }

    cout << "Command: " << "Submsg:" << submsg << " Mp1=" << mp1 << " Mp2=" << mp2 << endl;
    return kTRUE;
}

// --------------------------------------------------------------------------
//
// Process the messages from the GUI
//
Bool_t MStatusDisplay::ProcessMessage(Long_t msg, Long_t mp1, Long_t mp2)
{
    switch (GET_MSG(msg))
    {
    case kC_COMMAND:
        return ProcessMessageCommand(GET_SUBMSG(msg), mp1, mp2);
    }

    cout << "Msg: " << GET_MSG(msg) << " Submsg:" << GET_SUBMSG(msg);
    cout << " Mp1=" << mp1 << " Mp2=" << mp2 << endl;

    return kTRUE;
}

void MStatusDisplay::CloseWindow()
{
    // Got close message for this MainFrame. Calls parent CloseWindow()
    // (which destroys the window) and terminate the application.
    // The close message is generated by the window manager when its close
    // window menu item is selected.

    // CloseWindow must be overwritten because otherwise CloseWindow
    // and the destructor are calling DestroyWindow which seems to be
    // in conflict with the TRootEmbeddedCanvas.
    delete this;
}

// --------------------------------------------------------------------------
//
// Calls SetBit(kNoContextMenu) for all TCanvas objects found in the
// Tabs.
//
void MStatusDisplay::SetNoContextMenu(Bool_t flag)
{
    flag ? SetBit(kNoContextMenu) : ResetBit(kNoContextMenu);
    for (int i=1; i<fTab->GetNumberOfTabs(); i++)
    {
        TCanvas *c = GetCanvas(i);
        if (c)
            flag ? c->SetBit(kNoContextMenu) : c->ResetBit(kNoContextMenu);
    }
}

// --------------------------------------------------------------------------
//
// Updates the canvas (if existing) in the currenly displayed Tab.
// The update intervall is controlled by StartUpdate and StopUpdate
//
Bool_t MStatusDisplay::HandleTimer(TTimer *timer)
{
    const Int_t c = fTab->GetCurrent();

    // Skip Legend Tab
    if (c==0)
        return kTRUE;

    // Update a canvas tab (if visible)
    if (timer==&fTimer && c!=fLogIdx)
    {
        UpdateTab(fTab->GetCurrentContainer());
        return kTRUE;
    }

    // update the logbook tab (if visible)
    if (timer==&fLogTimer && c==fLogIdx)
    {
        fLog->UpdateGui();

        if (!fLogBox->TestBit(kHasChanged))
            return kTRUE;

        fLogBox->MapSubwindows();
        fLogBox->Layout();
        fLogBox->ResetBit(kHasChanged);
        return kTRUE;
    }

    return kTRUE;
}

// --------------------------------------------------------------------------
//
// Draws a clone of a canvas into a new canvas. Taken from TCanvas.
//
void MStatusDisplay::DrawClonePad(TCanvas &newc, const TCanvas &oldc) const
{
    //copy pad attributes
    newc.Range(oldc.GetX1(),oldc.GetY1(),oldc.GetX2(),oldc.GetY2());
    newc.SetTickx(oldc.GetTickx());
    newc.SetTicky(oldc.GetTicky());
    newc.SetGridx(oldc.GetGridx());
    newc.SetGridy(oldc.GetGridy());
    newc.SetLogx(oldc.GetLogx());
    newc.SetLogy(oldc.GetLogy());
    newc.SetLogz(oldc.GetLogz());
    newc.SetBorderSize(oldc.GetBorderSize());
    newc.SetBorderMode(oldc.GetBorderMode());
    ((TAttLine&)oldc).Copy((TAttLine&)newc);
    ((TAttFill&)oldc).Copy((TAttFill&)newc);
    ((TAttPad&)oldc).Copy((TAttPad&)newc);

    //copy primitives
    TObject *obj;
    TIter next(oldc.GetListOfPrimitives());
    while ((obj=next())) {
        gROOT->SetSelectedPad(&newc);
        newc.GetListOfPrimitives()->Add(obj->Clone(),obj->GetDrawOption());
    }
    newc.Modified();
    newc.Update();
}

// --------------------------------------------------------------------------
//
// Reads the contents of a saved MStatusDisplay from a file.
//
Int_t MStatusDisplay::Read(const char *name)
{
    if (!gFile)
    {
        *fLog << warn << "MStatusDisplay::Read: No file found. Please create a TFile first." << endl;
        return 0;
    }

    if (!gFile->IsOpen())
    {
        *fLog << warn << "MStatusDisplay::Read: File not open. Please open the TFile first." << endl;
        return 0;
    }

    TObjArray list;

    const Int_t n = list.Read(name);
    if (n==0)
    {
        *fLog << warn << "MStatusDisplay::Read: No objects read." << endl;
        return 0;
    }

    TIter Next(&list);
    TCanvas *c;
    while ((c=(TCanvas*)Next()))
        DrawClonePad(AddTab(c->GetName()), *c);

    *fLog << inf << "MStatusDisplay: Key " << name << " with " << n << " keys read from file." << endl;

    return n;
}

// --------------------------------------------------------------------------
//
// Writes the contents of a MStatusDisplay to a file.
//
Int_t MStatusDisplay::Write(Int_t num, const char *name, Int_t option, Int_t bufsize)
{
    if (!gFile)
    {
        *fLog << warn << "MStatusDisplay::Write: No file found. Please create a TFile first." << endl;
        return 0;
    }

    if (!gFile->IsOpen())
    {
        *fLog << warn << "MStatusDisplay::Write: File not open. Please open the TFile first." << endl;
        return 0;
    }

    if (!gFile->IsWritable())
    {
        *fLog << warn << "MStatusDisplay::Write: File not writable." << endl;
        return 0;
    }

    if (num==0)
    {
        *fLog << warn << "MStatusDisplay::Write: Tab doesn't contain an embedded Canvas... skipped." << endl;
        return 0;
    }

    if (num>=fTab->GetNumberOfTabs())
    {
        *fLog << warn << "MStatusDisplay::Write: Tab doesn't exist... skipped." << endl;
        return 0;
    }

    TObjArray list;

    const Int_t from = num<0 ? 1 : num;
    const Int_t to   = num<0 ? fTab->GetNumberOfTabs() : num+1;

    TCanvas *c;
    for (int i=from; i<to; i++)
        if ((c = GetCanvas(i)))
            list.Add(c);

    const Int_t n = list.Write(name, kSingleKey);

    *fLog << inf << "MStatusDisplay: " << n << " keys written to file as key " << name << "." << endl;

    return n;
}

// --------------------------------------------------------------------------
//
// Use this to start the synchronous (GUI eventloop driven) tab update.
// Can also be used to change the update intervall. If millisec<0
// the intervall given in SetUpdateTime is used. If the intervall in
// SetUpdateTime is <0 nothing is done. (Call SetUpdateTime(-1) to
// disable the automatic update in a MEventloop.
//
void MStatusDisplay::StartUpdate(Int_t millisec)
{
    if (fTimer.GetTime()<TTime(0))
        return;
    fTimer.Start(millisec);
}

// --------------------------------------------------------------------------
//
// Stops the automatic GUI update
//
void MStatusDisplay::StopUpdate()
{
    fTimer.Stop();
}

// --------------------------------------------------------------------------
//
// Set the update interval for the GUI update, see StartUpdate.
//
void MStatusDisplay::SetUpdateTime(Long_t t)
{
    fTimer.SetTime(t);
}

// --------------------------------------------------------------------------
//
// Set the background color in a canvas
//
void MStatusDisplay::CanvasSetFillColor(TPad &p, Int_t col) const
{
    TObject *obj;

    TIter Next(p.GetListOfPrimitives());
    while ((obj=Next()))
    {
        if (obj->InheritsFrom(TPad::Class()))
            CanvasSetFillColor(*(TPad*)obj, col);
        if (obj->InheritsFrom(TFrame::Class()))
            ((TFrame*)obj)->SetFillColor(col);
    }

    p.SetFillColor(col);
}

void MStatusDisplay::AddExtension(TString &name, const TString &ext, Int_t num) const
{
    if (name.IsNull())
    {
        name = "status";
        if (num>0)
        {
            name += "-";
            name += num;
        }
    }

    if (name.EndsWith("."+ext))
        return;

    name += ".";
    name += ext;
}

// --------------------------------------------------------------------------
//
// In case of num<0 all tabs are written into the PS file. If num>0
// the canvas in the corresponding tab is written to the file.
// Name is the name of the file (with or without extension).
//
// Returns the number of pages written.
//
// To write all tabs you can also use SaveAsPS(name)
//
Int_t MStatusDisplay::SaveAsPS(Int_t num, TString name) const
{
    if (num>=fTab->GetNumberOfTabs())
    {
        *fLog << warn << "Tab #" << num << " doesn't exist..." << endl;
        return 0;
    }
    if (num==0)
    {
        *fLog << warn << "Tab #" << num << " doesn't contain an embedded canvas..." << endl;
        return 0;
    }
    if (fTab->GetNumberOfTabs()<2 || !gPad)
    {
        *fLog << warn << "Sorry, you must have at least one existing canvas (gPad!=NULL)" << endl;
        return 0;
    }

    AddExtension(name, "ps", num);

    if (num<0)
        *fLog << inf << "Open ps-File: " << name << endl;

    TPad       *padsav = (TPad*)gPad;
    TVirtualPS *psave  = gVirtualPS;

    TPostScript ps(name, 112);
    ps.SetBit(TPad::kPrintingPS);
    ps.PrintFast(13, "/nan {1} def ");

    gVirtualPS = &ps;

    //
    // Create a list to delete the canvas clones
    //
    TList l;
    l.SetOwner();

    //
    // Create some GUI elements for a page legend
    //
    TLine line;

    int page = 1;

    //
    // Maintain tab numbers
    //
    const Int_t from = num<0 ? 1 : num;
    const Int_t to   = num<0 ? fTab->GetNumberOfTabs() : num+1;

    for (int i=from; i<to; i++)
    {
        TCanvas *c;
        if (!(c = GetCanvas(i)))
        {
            if (num<0)
                *fLog << inf << " - ";
            *fLog << "Tab #" << i << " doesn't contain an embedded Canvas... skipped." << endl;
            continue;
        }

        //
        // Init page and page size, make sure, that the canvas in the file
        // has the same Aspect Ratio than on the screen.
        //
        ps.NewPage();

        Float_t psw = 26; // A4 - width
        Float_t psh = 20; // A4 - height

        const Float_t cw = c->GetWw();
        const Float_t ch = c->GetWh();

        if (psw/psh>cw/ch)
            psw = cw/ch*psh;
        else
            psh = ch/cw*psw;

        ps.Range(psw, psh); // A4

        //
        // Clone canvas and change background color and schedule for
        // deletion
        //
        TCanvas *n = (TCanvas*)c->Clone();
        CanvasSetFillColor(*n, kWhite);
        l.Add(n);

        //
        // Paint canvas into root file
        //
        if (num<0)
            *fLog << inf << " - ";
        *fLog << inf << "Writing Tab #" << i << ": " << c->GetName() << " (" << n << ") ";
        if (num>0)
            *fLog << "to " << name;
        *fLog << "..." << flush;

        n->SetBatch(kTRUE);
        n->Paint();

        //
        // Use the canvas as coordinate system for the overlaying text
        //
        gPad = n;

        //
        // Print overlaying text (NDC = %)
        //
        ps.SetTextColor(kBlack);
        ps.SetTextSize(0.015);
        ps.SetTextFont(22);
        ps.SetTextAlign(11); // left top
        ps.TextNDC(0, 1.02, TString("  ")+n->GetName());
        ps.SetTextAlign(21); // cent top
        ps.TextNDC(0.5, 1.02, "MARS - Magic Analysis and Reconstruction Software");
        ps.SetTextAlign(31); // right top
        ps.TextNDC(1, 1.02, Form("Page No.%i (%i)  ", page++, i));
        line.PaintLineNDC(0, 1.015, 1, 1.015);

        //
        // Finish drawing page
        //
        n->SetBatch(kFALSE);
        if (num<0)
            *fLog << "done." << endl;
    }

    gPad = NULL; // Important!

    l.Delete();

    ps.Close();

    gVirtualPS = psave;
    padsav->cd();

    *fLog << inf << "done." << endl;

    return page-1;
}

/*
void MStatusDisplay::SaveAsGIF(Int_t num, TString name) const
{
    AddExtension(name, "gif", num);

    cout << "Open gif-File: " << name << endl;
    cout << " SORRY, not implemented." << endl;
}

void MStatusDisplay::SaveAsC(Int_t num, TString name) const
{
    AddExtension(name, "C", num);

    cout << "Open C-File: " << name << endl;
    cout << " SORRY, not implemented." << endl;
}
*/

// --------------------------------------------------------------------------
//
// In case of num<0 all tabs are written into the PS file. If num>0
// the canvas in the corresponding tab is written to the file.
// Name is the name of the file (with or without extension).
//
// Returns the number of keys written.
//
// To write all tabs you can also use SaveAsPS(name)
//
Int_t MStatusDisplay::SaveAsRoot(Int_t num, TString name)
{
    if (num>=fTab->GetNumberOfTabs())
    {
        *fLog << warn << "Tab #" << num << " doesn't exist..." << endl;
        return 0;
    }
    if (num==0)
    {
        *fLog << warn << "Tab #" << num << " doesn't contain an embedded canvas..." << endl;
        return 0;
    }
    if (fTab->GetNumberOfTabs()<2 || !gPad)
    {
        *fLog << warn << "Sorry, you must have at least one existing canvas." << endl;
        return 0;
    }

    AddExtension(name, "root", num);

    TFile *fsave = gFile;
    TFile file(name, "RECREATE", "MARS - Status Window Contents", 9);
    const Int_t keys = Write(num);
    gFile = fsave;

    return keys;
}
