/*
 * gui_rest.cpp  --  Part of the CinePaint plug-in "Bracketing_to_HDR"
 *
 * Copyright (c) 2005-2006  Hartmut Sbosny  <hartmut.sbosny@gmx.de>
 *
 * LICENSE:
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
/**
  @file  gui_rest.cpp   -  included by "gui.cxx" via a FLUID directive.
  
  Implementation of things declared in FLUID (->"gui.h"), but NOT implemented
   via FLUID (-->"gui.cxx"), as well as of things declared in "gui_rest.hpp".
   
  @note  Von ResponsePlot werden nur die nicht-leeren \a which Kurven geplottet.
   Nur die(se) dargestellten sollten auch durch die "Using Next"-Knoepfe offeriert
   werden. Modus des Aktivierens und Einschaltens: \c update_activation_radios_...()
   aktualisiert Aktivierung unabhaengig vom Einschaltwert, \c update_onoff_radios_...() 
   aktualisiert Einschaltwert unabhaengig von Aktivierung. 

  @note AllWindows::do_file_chooser() laesst fuer initpath=0 ein rescan() ausfuehren;
   wenn also zuerst ein Bildpfad gewaehlt wurde und dann mit einem curve_path()==0
   hineingegangen wird (->rescan()), kommt auch hier als Startverzeichnis der
   Bildpfad. Erst danach haben Bilder und Kurven eigene Startpfade. Falls die
   Preferenz-Pfade zum Einsatz kommen, natuerlich gleich separat. -- Derzeit werden
   als Startpfade die Fl_File_Chooser-Pfade gespeichert; man koennte auch den
   Dateinamen abschneiden und nur die Verzeichnisse speichern; File_Chooser stellte
   dann nicht gleich auf jenes Bild inklusive Vorschauaufwand.
   
  @todo gui.fl sollte zerlegt werden in mehrere .fl-Dateien, zu jeder Windowklasse am
   besten eine. Entsprechend wuerde auch gui_rest.cpp sich aufdroeseln. Zur Zeit das
   reinste Sammelsurium.
*/

/*  Some includes not realy needed because "we" are included by gui.cxx */
#include <FL/filename.H>                // FL_PATH_MAX
#include "gui.h"			// generated by FLUID
#include "gui_rest.hpp"                 
#include "AllWindows.hpp" 
#include "../bracketing_to_hdr.hpp"     // cpaint_load_image()
#include "../br_core/br_messages.hpp"   // v_alert()
#include "../br_core/testtools.hpp"     // for TestSuiteWinClass
#include "../br_core/DisplcmFinderCorrel_RGB.hpp" // for compute_offsets..()
#include "../br_core/i18n.hpp"          // macro _()


/* DECLARATIONS of LOCAL FUNCTIONS */
static bool     file_exists             (const char* fname);
static char*    get_curve_basepath      (char* path);
static void     message_read_error      (const char* fname);


/* LOCAL VARIABLES */
static bool overwrite_warning_ = true;


using namespace br;

////////////////////////////////////////////////////////////////
///
///  non-member functions  -  declared above or in "gui_rest.h" or in "gui.h"
///
////////////////////////////////////////////////////////////////

/**+*************************************************************************\n
  Returns true, if file \a fname exists, false otherwise.
******************************************************************************/
bool 
file_exists (const char* fname)
{
    if (!fname || !*fname) return false;

    FILE* f = fopen (fname,"r");
    if (!f) return false;
    
    fclose(f); 
    return true; 
}

/**+*************************************************************************\n
  Removes from \a path a ".txt" extension and a trailing "_R|G|B" if any. E.g.:
   -  "crv_R.txt" ---> "crv"
   -  "crv.txt"   ---> "crv"
   -  "crv_R.aaa" ---> "crv_R.aaa"  (No ".txt" extension!)
******************************************************************************/
char* 
get_curve_basepath (char* path)
{
    char* p = (char*) fl_filename_ext (path);
    if (strcmp(p,".txt") == 0) { 
      *p = '\0';                      // truncates extension
      if (p > path + 1) {             // "_R|G|B" possible?
        --p;                          // pos before '.'
        if ((*p=='R' || *p=='G' || *p=='B') && p[-1]=='_') {
          *(--p) = '\0';              // truncates "_R|G|B"
        }
      }
    }
    return path;
}

/**+************************************************************************\n
  message_read_error()   --    Provisonally. Should provide more information.
*****************************************************************************/
void
message_read_error (const char* fname)
{
    fl_alert (_("Could not read response curve file \"%s\"."), fname);
    //br::v_alert ("Could not read response curve file \"%s\".", fname);
}


/**+*************************************************************************\n
  Saves the response curve \a channel of kind \a which from \a theBr2Hdr into
   a file \a fname. A warning and a "what to do" dialog is opened, if a file of 
   this name already exists. GUI wrapper of Br2Hdr::write_ResponseCurve(..) for
   the sake of this "What to do"-dialog.
******************************************************************************/
bool 
save_response_curve (Br2Hdr::WhichCurves which, int channel, const char* fname)
{
    if (!fname || !*fname) return false;
        
    if (overwrite_warning_ && file_exists(fname)) 
    {
      char txt[256];
      snprintf (txt, 256, _("A file \"%s\" already exists."), fl_filename_name(fname));
      switch (fl_choice (txt, _("Cancel"), _("Overwrite"), _("Overwrite all")))
      {
        case 2:  overwrite_warning_ = false; break;
        case 1:  break;
        default: return false;
      }
    }
    return theBr2Hdr.write_ResponseCurve (which, channel, fname);
}

/**+*************************************************************************\n
  Opens a file_chooser dialog to save response curves of sort \a which from 
   the global \a theBr2Hdr. Depending on \a channel_like, all channels are saved
   or a single one. The selected filename is taken as basename in the following
   sense: For the red channel is added a "_R", for the green a "_G", for the
   blue a "_B" plus an extension ".txt". If the selected filename has already
   e.g. a trailing "_R.txt", this part is removed.
  DISADVANTAGE: Due to the automatic filename complement it is impossible to
   save a curve under an arbitrary filename. 
  
  @param which: Which kind of response curves (next_use, computed, external).
  @param channel_like: -1: save all curves;  >= 0: save curve for that channel.

  @todo The overwrite warning (in save_response_curve()) should appear above
   the open file_chooser dialog and in a loop, instead of after closing the
   file_chooser dialog.
******************************************************************************/
void 
save_response_curves_dialog (Br2Hdr::WhichCurves which, int channel_like)
{
    // which==CALCTOR_S nur voruebergehend und wird abgefangen
    IF_FAIL_DO (which < Br2Hdr::CALCTOR_S, return);
    
    if (allWins.do_file_chooser (allWins.curve_path(), "*.{txt}", 
        Fl_File_Chooser::CREATE, _("Save response curves under basename...")))
    {
      //  Get curve base path from selected path
      char  basepath [FL_PATH_MAX];
      strncpy (basepath, allWins.fileChooser->value(), FL_PATH_MAX);
      get_curve_basepath (basepath);
      //printf("basepath = %s\n", basepath);
      
      char  curvepath [FL_PATH_MAX];
      overwrite_warning_ = true;
      if (channel_like == 0 || channel_like == -1) {
        snprintf (curvepath, FL_PATH_MAX, "%s_R.txt", basepath);
        save_response_curve (which, 0, curvepath);
      }
      if (channel_like == 1 || channel_like == -1) {
        snprintf (curvepath, FL_PATH_MAX, "%s_G.txt", basepath);
        save_response_curve (which, 1, curvepath);
      }
      if (channel_like == 2 || channel_like == -1) {
        snprintf (curvepath, FL_PATH_MAX, "%s_B.txt", basepath);
        save_response_curve (which, 2, curvepath);
      }
      //  Save selected path as start point for next call
      allWins.curve_path (allWins.fileChooser->value());
    }
}

/**+*************************************************************************\n
  Reads a response curve from file \a fname for channel \a channel and updates 
   the GUI. Used by the load_dialogs as well as the automatic response loading 
   at the programm start (that's why we have to check \a channel and \a fname). 
   Gives a GUI error message, if a failure occured.
******************************************************************************/
bool 
load_response_curve (int channel, const char* fname)
{
    if (channel < 0 || channel > 2 || !fname || !*fname) 
      return false;
    
    if (! theBr2Hdr.read_ResponseCurve (channel, fname))
    {
      message_read_error (fl_filename_name(fname));
      return false;
    }
    //  Update the "using next" group and the curve plot; update_onoff_radios()
    //   nessecary, since loaded curves are applied autom. if use_extern==1.
    allWins.response -> update_activation_group_Using_Next();  
    allWins.response -> update_activation_radios_External();
    allWins.response -> update_onoff_radios_Using_Next(); 
    allWins.response -> do_which_curves (Br2Hdr::EXTERNAL);  // plot external curves
    return true;
}

/**+*************************************************************************\n
  Opens a file dialog and reads the response curve for \a channel. Currently 
   used only in ResponseWinClass, could be included there as callback function.
  Idea: For isUseExternResponse()==TRUE we could apply a loaded curve automat. 
******************************************************************************/
void 
load_response_curve_dialog (int channel)
{
    if (channel < 0 || channel > 2) return;
    
    char title [64];
    char ch[3] = {'R','G','B'};
    sprintf (title, _("Load response curve for channel %c"), ch[channel]);
    
    if (allWins.do_file_chooser (allWins.curve_path(), "*.{txt}", Fl_File_Chooser::SINGLE, title))
    {
      load_response_curve (channel, allWins.fileChooser->value());
      //  Save selected path as start point for next call
      allWins.curve_path (allWins.fileChooser->value());
    }     
}

/**+*************************************************************************\n
  Opens a file dialog, takes the curve *base* path from the selected path and
   tries to read all three response curves for the completed paths. Currently 
   used only in ResponseWinClass, could be included there as callback function.
******************************************************************************/
void 
load_all_response_curves_dialog()
{
    printf("curve_path = \"%s\"\n", allWins.curve_path());
    if (allWins.do_file_chooser (allWins.curve_path(), "*.{txt}", 
        Fl_File_Chooser::SINGLE, _("Load all response curves. Choose basename")))
    {
      //  Get curve base path from selected path
      char  basepath [FL_PATH_MAX];
      strncpy (basepath, allWins.fileChooser->value(), FL_PATH_MAX);
      get_curve_basepath (basepath);
      printf("basepath = %s\n", basepath);
      
      char  curvepath [FL_PATH_MAX];
      snprintf (curvepath, FL_PATH_MAX, "%s_R.txt", basepath);
      load_response_curve (0, curvepath);
      
      snprintf (curvepath, FL_PATH_MAX, "%s_G.txt", basepath);
      load_response_curve (1, curvepath);

      snprintf (curvepath, FL_PATH_MAX, "%s_B.txt", basepath);
      load_response_curve (2, curvepath);
      
      //  Save selected path as start point for next call
      allWins.curve_path (allWins.fileChooser->value());
    }     
}

  
//////////////////////////////////////////////////////////////////
///
///   MainWinClass  -  declared in "gui.h", i.e. in gui.fl.
///
//////////////////////////////////////////////////////////////////

/**+*************************************************************************\n
  Opens a file dialog for input images, loads the images, inserts them into 
   \a theBr2Hdr's image container; finally, if size_active has changed, we set
   times setting to BY_EXIF and call endAddImages(force=TRUE), which sets the 
   times accordingly and inits \a theBr2Hdr's calctor.
  
  @note Bei Raw-Dateien erscheint der rawphoto Plugin-Dialog. Damit bei mehreren
   ausgewaehlten Bilder er nicht jedesmal erscheint, schalten wir ab dem zweiten Bild
   auf noninteractive (fuer andere Fileformate irrelevant). Wenn im rawphoto-Dialog
   allerdings "Abbruch" gedrueckt wird, wird zwar dieses Bild nicht geladen, jedoch 
   weiter die folgenden. Wenn Dialog da ausgeschaltet, waere Einfluss dahin. Daher
   schalten wir auf noninteractive nur, wenn cpaint_load_image() TRUE returnierte.
  @todo Wenn das erste Bild kein Raw-Format, erst das zweite, wird Dialog
   dennoch dann unterbunden. Dies zu verhindern muesste Extension abgefragt werden.
******************************************************************************/
void 
MainWinClass::load_input_images()
{
    int count = allWins.do_file_chooser (allWins.image_path(), 
                  "Image Files (*.{tif,jpg,png,bmp,gif})",  
                  Fl_File_Chooser::MULTI, _("Open Images for Bracketing to HDR"),
                  allWins.image_filter_value());
                  
    printf ("Number of selected files = %d\n", count);
/*    printf ("filter= \"%s\",   filter_value= %d\n", 
        allWins.fileChooser->filter(), allWins.fileChooser->filter_value());*/
  
    int old_size_active_ = theBr2Hdr.size_active();
  
    /*  Load selected images and insert them into `theBr2Hdr' */
    bool  inter = true;   // interactive
    for (int i=1; i <= count; i++) 
    {
      inter = !cpaint_load_image (allWins.fileChooser->value(i), theBr2Hdr, inter);
    }
    
    if (theBr2Hdr.size_active() != old_size_active_) {
      theBr2Hdr.timesSetting (Br2Hdr::BY_EXIF);  // try EXIF data
      theBr2Hdr.endAddImages (true);  // force calctor creation
    }
    
    /*  Save selected path and filter value as start point for next call */
    if (count) {
      allWins.image_path (allWins.fileChooser->value());
      allWins.image_filter_value (allWins.fileChooser->filter_value());
    }
}

/**+*************************************************************************\n
  Sets mode for solving `Ax=b' in \a theBr2Hdr and updates the GUI. Intended
   for usage by read_preferences().
******************************************************************************/
void 
MainWinClass::solve_mode (ResponseSolverBase::SolveMode mode)
{
    theBr2Hdr.solve_mode (mode);
    
    switch (mode)
    {
    case ResponseSolverBase::AUTO:    mbar_item_solvemode_auto_->setonly(); break;
    case ResponseSolverBase::USE_QR:  mbar_item_solvemode_use_qr_->setonly(); break;
    case ResponseSolverBase::USE_SVD: mbar_item_solvemode_use_svd_->setonly(); break;
    default: ;
    }
}

/**+*************************************************************************\n
  Restores the default settings.  
  @todo Originale Defaultwerte zur Zeit hier nur erinnert, Identitaet ungesichert,
   jede einen Wert haltende Stelle sollte besser ein "default_value()" offerieren,
   der hier erfragt werden koennte. Und wenn Defaultwert per FLUID eingestellt?
******************************************************************************/
void 
MainWinClass::default_settings()
{
    set_show_offset_buttons (true);
    set_auto_update_followup (true);
    set_auto_load_response (true);
    solve_mode (ResponseSolverBase::AUTO);
    method_Z (1);
    mbar_item_downsample_U16_ -> clear();
    theBr2Hdr.setUseExternResponse (false);
    set_auto_save_prefs (false);
}


/**+*************************************************************************\n
  handle_Event()

  - De/activates menubar items accordingly to changes of state of theBr2Hdr at
     every event.
  - Blends-in the timesbar at a SIZE event if not all times are valid.
  - Updates at a TIMES_CHANGED event the timesbar switches accordingly to the
     theBr2Hdr's settings.
******************************************************************************/
void 
MainWinClass::handle_Event (Br2Hdr::Event e)
{
#ifdef BR_DEBUG_RECEIVER
    printf("MainWinClass::");  EventReceiver::handle_Event(e);
#endif 
    
    //  Shorter names for the menu_item variables
    Fl_Menu_Item* item_Init     = mbar_item_Init_Calctor_;
    Fl_Menu_Item* item_Response = mbar_item_Compute_Response_;
    Fl_Menu_Item* item_HDR      = mbar_item_Compute_HDR_;
    Fl_Menu_Item* item_logHDR   = mbar_item_Compute_logHDR_;

    if (theBr2Hdr.size_active() > 1) {
      if (! item_Init->active()) {
        item_Init->activate();
      }
    }else {
      if (item_Init->active()) {
        item_Init->deactivate();
      }
    }
    
    if (theBr2Hdr.calctor()) {
      if (! item_Response->active()) {
        item_Response->activate();
        item_HDR->activate();
        item_logHDR->activate();
      }
    }else {
      if (item_Response->active()) {
        item_Response->deactivate();
        item_HDR->deactivate();
        item_logHDR->deactivate();
      }
    }
    
    switch (e) 
    {
    case Br2Hdr::IMG_VECTOR_SIZE:
    case Br2Hdr::SIZE_ACTIVE:
        /*  Blend-in the time setting bar if not all times are valid */
        if (! theBr2Hdr.imgVector().have_times()) {
          timesbar_on();
          mbar_item_timesbar_ -> set();
        }
        break;
    
    case Br2Hdr::TIMES_CHANGED:
        /*  Set timesbar switches accord. to current theBr2Hdr's settings */
        tggl_normedTimes_ -> value (theBr2Hdr.normedTimes());
        
        switch (theBr2Hdr.timesSetting()) {
          case Br2Hdr::BY_EXIF: 
              //printf("BY_EXIF\n");
              radio_timesByEXIF_ -> setonly();
              break;
          case Br2Hdr::BY_STOP:
              //printf("BY_STOP\n");
              if (theBr2Hdr.forActiveTimes())
                   radio_timesActiveByStop_ -> setonly();
              else radio_timesByStop_ -> setonly(); 
              break;
          case Br2Hdr::BY_NONE:  // switch off all radio buttons
              //printf("BY_NONE\n");
              radio_timesByEXIF_ -> clear();
              radio_timesByStop_ -> clear();
              radio_timesActiveByStop_ -> clear();
              break;
        }
        
        if (theBr2Hdr.timesSetting() == Br2Hdr::BY_STOP)
             stopvalueChoicer_ -> activate();
        else stopvalueChoicer_ -> deactivate();
         
        break;
        
    default:;
    }
}


#if 0
//////////////////////////////////////////////////////////////////
///
///   MainWinMenubarWatcher  -  declared in "gui_rest.hpp".
///
//////////////////////////////////////////////////////////////////

/**+*************************************************************************\n
  handle_Event()

  De/activation of menubar items accordingly to changes of the state of
   Br2Hdr::Instance().

  Reference to menu items happens via FLUID named items. Per default the menu
   structure as well as these variables are *static* class members. Here ok
   as we have only a single instance. For multiple menubar instances we would
   have to take local copies via `o->copy(o->menu())´ ("Extra Code" field of
   the menubar in FLUID), and to refer then to `o->menu() + diff´.
******************************************************************************/
void 
MainWinMenubarWatcher::handle_Event (Br2Hdr::Event e)
{
#ifdef BR_DEBUG_RECEIVER
    printf("MainWinMenubarWatcher::");  EventReceiver::handle_Event(e);
#endif 
    
    //  Shorter names for the menu_item variables
    Fl_Menu_Item* item_Init     = MainWinClass::mbar_item_Init_Calctor_;
    Fl_Menu_Item* item_Response = MainWinClass::mbar_item_Compute_Response_;
    Fl_Menu_Item* item_HDR      = MainWinClass::mbar_item_Compute_HDR_;
    Fl_Menu_Item* item_logHDR   = MainWinClass::mbar_item_Compute_logHDR_;

    if (Br2Hdr::Instance().size_active() > 1) {
      if (! item_Init->active()) {
        item_Init->activate();
      }
    }else {
      if (item_Init->active()) {
        item_Init->deactivate();
      }
    }
    
    if (Br2Hdr::Instance().calctor()) {
      if (! item_Response->active()) {
        item_Response->activate();
        item_HDR->activate();
        item_logHDR->activate();
      }
    }else {
      if (item_Response->active()) {
        item_Response->deactivate();
        item_HDR->deactivate();
        item_logHDR->deactivate();
      }
    }
}
#endif

//////////////////////////////////////////////////////////////////
///
///   ResponseWinClass  -  declared in "gui.h", i.e. in gui.fl.
///
//////////////////////////////////////////////////////////////////

/**+*************************************************************************\n
  handle_Event()
   Updates widget for some events. For EXTERN_RESPONSE and CCD_UPDATED copying 
   (applying) to Br2Hdr's use_next curves has done already by the Manager.
   The EXTERN_RESPONSE event was introduced to allow `use_extern´ switchings
   also from outside of ResponseWinClass. If outside unused, it could be removed.
   Meanwhile outside used by read_preferences().
******************************************************************************/
void 
ResponseWinClass::handle_Event (Br2Hdr::Event e)
{
#ifdef BR_DEBUG_RECEIVER
    printf("ResponseWinClass::");  EventReceiver::handle_Event(e);
#endif
    switch (e)
    {
    case Br2Hdr::EXTERN_RESPONSE:
        BR_EVENT_HANDLED(e);
        update_activation_group_Using_Next();
        if (theBr2Hdr.isUseExternResponse()) {
          slider_grid_points_->deactivate();
          slider_smoothing_->deactivate();
          update_onoff_radios_Using_Next();
          do_which_curves (Br2Hdr::USE_NEXT);   // show "use_next" curves
        }
        else {
          slider_grid_points_->activate();
          slider_smoothing_->activate();
          plot_->update();      // "weight fncts to plot" could be changed
        }
        toggle_use_extern_curves_->value (theBr2Hdr.isUseExternResponse());
        break;        
      
    case Br2Hdr::CCD_UPDATED:       // understood as "new curves COMPUTED"
        BR_EVENT_HANDLED(e);
        update_activation_radios_Computed();
        update_onoff_radios_Using_Next();
        do_which_curves (Br2Hdr::COMPUTED);   // show computed curves
        break;
            
    case Br2Hdr::CALCTOR_DELETED:
        BR_EVENT_HANDLED(e);
        if (plot_->which_curves()==Br2Hdr::CALCTOR_S) 
          plot_->update();
        break;    
            
    case Br2Hdr::CCD_OUTDATED:          // only an "outdated" box is to add
        BR_EVENT_HANDLED(e);
        if (plot_->add_outdated_box())  
          plot_->redraw();
        break;
    
    case Br2Hdr::WEIGHT_CHANGED:
    case Br2Hdr::CALCTOR_INIT:    
        BR_EVENT_HANDLED(e);
        plot_->update();
        break;
        
    default: BR_EVENT_NOT_HANDLED(e);
        break;
    }            
}

/**+*************************************************************************\n
  Updates activation of the whole "using next" group. Activates, if any external 
   curve exists OR isUseExternResponse() == True ; else deactivates.
******************************************************************************/
void 
ResponseWinClass::update_activation_group_Using_Next()
{
    if (! theBr2Hdr.isExternResponseEmpty() || theBr2Hdr.isUseExternResponse()) 
      group_using_next_->activate();
    else
      group_using_next_->deactivate();
} 

/**+*************************************************************************\n
  Updates activation of "using next" buttons for the computed response curves.
   Independently of On/Off values!
******************************************************************************/
void 
ResponseWinClass::update_activation_radios_Computed()
{
    int sum=0;
    for (int i=0; i < 3; i++)
    {
      if (theBr2Hdr.getResponseCurveComputed(i).is_empty())
        {radio_computed_[i]->deactivate();}
      else
        {radio_computed_[i]->activate();  sum++;}
    }
    //  Activate the "all button" if any curve exists, else deactivate
    if (sum) button_computed_all_->activate();
    else     button_computed_all_->deactivate();
}

/**+*************************************************************************\n
  Updates activation of "using next" buttons for the external response curves.
   Independently of On/Off values!
******************************************************************************/
void 
ResponseWinClass::update_activation_radios_External()
{
    int sum=0;
    for (int i=0; i < 3; i++)
    {
      if (theBr2Hdr.getResponseCurveExtern(i).is_empty())
        {radio_extern_[i]->deactivate();}
      else
        {radio_extern_[i]->activate();  sum++;}
    }
    //  Activate the "all button" if any curve exists, else deactivate 
    if (sum) button_extern_all_->activate();  
    else     button_extern_all_->deactivate();
}

/**+*************************************************************************\n
  Updates the On/Off values of all "using next" radio buttons accordingly to the
   state of \a theBr2Hdr's use_next curves. Independently of activation status!
******************************************************************************/
void 
ResponseWinClass::update_onoff_radios_Using_Next()
{
    for (int i=0; i<3; i++) {
      if (! theBr2Hdr.getResponseCurveUsenext(i).is_empty())
        if (theBr2Hdr.isUsenextCurveComputed(i)) radio_computed_[i]->setonly();
        else                                      radio_extern_[i]->setonly();
      else {
        radio_computed_[i]->clear();
        radio_extern_[i]->clear();
      }  
    }  
}

/**+*************************************************************************\n
  Sets the "plotted curves" choice to value \a which, updates the plot and 
   updates the save_menu accordingly.
******************************************************************************/
void 
ResponseWinClass::do_which_curves (Br2Hdr::WhichCurves which)
{
    choice_plot_->value (which);
    plot_->which_curves (which);
    plot_->update();
    update_save_menu();
}

/**+*************************************************************************\n
  Applies a computed response curve (or all) and updates the widget.
  @param channel_like: >= 0: apply the curve for that channel; < 0: apply all 
    channels. 
******************************************************************************/
void 
ResponseWinClass::applyResponseCurveComputed (int channel_like)
{
    if (channel_like >= 0) 
      theBr2Hdr.applyResponseCurveComputed (channel_like);
    else 
      for (int i=0; i < 3; i++) 
        theBr2Hdr.applyResponseCurveComputed (i);
    
    update_onoff_radios_Using_Next();
    do_which_curves (Br2Hdr::USE_NEXT);   // show the "use_next" curves
}

/**+*************************************************************************\n
  Applies an external response curve (or all) and updates the widget.
  @param channel_like: >= 0: apply the curve for that channel; < 0: apply all 
    channels. 
******************************************************************************/
void 
ResponseWinClass::applyResponseCurveExtern (int channel_like)
{
    if (channel_like >= 0)
      theBr2Hdr.applyResponseCurveExtern (channel_like);
    else 
      for (int i=0; i < 3; i++) 
        theBr2Hdr.applyResponseCurveExtern (i);
    
    update_onoff_radios_Using_Next();
    do_which_curves (Br2Hdr::USE_NEXT);   // show the "use_next" curves
}

/**+*************************************************************************\n
  De/Activates the save_menu accordingly to the current plotted curves.
******************************************************************************/
void 
ResponseWinClass::update_save_menu()
{
    //  For the transient case which==CALCTOR_S we don't provide saving
    if (plot_->which_curves() == Br2Hdr::CALCTOR_S) {
      menubttn_save_->deactivate();
      return;
    }
    if (theBr2Hdr.isResponseEmpty (plot_->which_curves()))
      menubttn_save_->deactivate();
    else {
      menubttn_save_->activate();
#if 0      
      //  So, if we could use in FLUID array-names for static objects:
      for (int i=0; i<3; i++) 
        if (theBr2Hdr.getResponseCurve (plot_->which_curves(), i).is_empty())
          //menubttn_save_item_channel_[i]->deactivate();
          menubttn_save_item_channel_[i]->hide();
        else
          //menubttn_save_item_channel_[i]->activate();
          menubttn_save_item_channel_[i]->show();
#else          
      //  Work around a FLUID bug (wrong initialization of static arrays):
      if (theBr2Hdr.getResponseCurve (plot_->which_curves(), 0).is_empty())
           menubttn_save_item_channel_0->hide();
      else menubttn_save_item_channel_0->show();
      
      if (theBr2Hdr.getResponseCurve (plot_->which_curves(), 1).is_empty())
           menubttn_save_item_channel_1->hide();
      else menubttn_save_item_channel_1->show();
      
      if (theBr2Hdr.getResponseCurve (plot_->which_curves(), 2).is_empty())
           menubttn_save_item_channel_2->hide();
      else menubttn_save_item_channel_2->show();
#endif          
    }
}


/**+*************************************************************************\n
  Updates widget after the set of external response curves has been cleared.
******************************************************************************/
void 
ResponseWinClass::update_after_clear_response_extern()
{
    update_activation_group_Using_Next(); // could be deactivated
    update_activation_radios_External();  // should be deactivated
    update_onoff_radios_Using_Next();
    do_which_curves (plot_->which_curves()); // previous `which´
    // The last is a shortening for update_save_menu() + plot_->update().
}


//////////////////////////////////////////////////////////////////
///
///   HistogramWinClass  -  declared in "gui.h", i.e. in gui.fl.
///
//////////////////////////////////////////////////////////////////

/**+*************************************************************************\n
  handle_Event()  --  updates widget for some events.
******************************************************************************/
void 
HistogramWinClass::handle_Event (Br2Hdr::Event e)
{
#ifdef BR_DEBUG_RECEIVER
    printf("HistogramWinClass::"); EventReceiver::handle_Event(e);
#endif
    switch (e)
    {
    case Br2Hdr::IMG_VECTOR_SIZE:
        BR_EVENT_HANDLED(e);
        if (plot_->image() >= theBr2Hdr.size()) 
          plot_->set_image(0);
        build_choice_image();
        plot_->update();
        break;
        
    default: BR_EVENT_NOT_HANDLED(e);
        break;
    }            
}

/**+*************************************************************************\n
  Builds a choice menu for as many images as in \a theBr2Hdr's image container.
******************************************************************************/
void 
HistogramWinClass::build_choice_image()
{
    choice_image_->clear();              // no redraw()
    if (theBr2Hdr.size()) 
    {
      for (int i=0; i < theBr2Hdr.size(); i++) 
      {
        char s[4];
        snprintf(s,4,"%d", i+1);         // begin with "1"
        choice_image_->add(s,0,0);
      }
      choice_image_->value (plot_->image()); // implies a redraw()
    }
    else 
      choice_image_->redraw();           // redraw to clear()
}


//////////////////////////////////////////////////////////////////
///
///   TestSuiteWinClass  -  declared in "gui.h", i.e. in gui.fl.
///
//////////////////////////////////////////////////////////////////

/**+*************************************************************************\n
  Helper of Ctor (to reduce things I have to code in FLUID). Sets the default
   parameters for simulated response functions.
******************************************************************************/
void TestSuiteWinClass::init_params()
{
    param_resp_lin_.Xmin_R = 0.1;  param_resp_lin_.Xmax_R = 1.0;
    param_resp_lin_.Xmin_G = 0.1;  param_resp_lin_.Xmax_G = 2.0;
    param_resp_lin_.Xmin_B = 0.1;  param_resp_lin_.Xmax_B = 3.0;
    
    param_resp_cos_.Xmin_R = 0.1;  param_resp_cos_.Xmax_R = 1.0;
    param_resp_cos_.Xmin_G = 0.1;  param_resp_cos_.Xmax_G = 2.0;
    param_resp_cos_.Xmin_B = 0.1;  param_resp_cos_.Xmax_B = 3.0;
}

/**+*************************************************************************\n
  Reads out the GUI objects, creates the simulated response functions, creates
   the images and adds them to the manager - wrapped by begin--endAddImages().
******************************************************************************/
void TestSuiteWinClass::do_add()
{
    /*  Read some values from the GUI objects */
    int xdim = int(input_xdim_->value() + 0.5);
    int ydim = int(input_ydim_->value() + 0.5);
    int N    = int(input_N_->value() + 0.5);
    ImageType type = ImageType(choice_data_type_->value() + 1);
    ResponseShape resp_shape = ResponseShape(choice_response_->value());
    RadianceSceneShape scene_shape = RadianceSceneShape(choice_scene_->value());
    
    /*  Set zmax accordingly to data type */
    unsigned zmax;
    switch (type) {
      case IMAGE_RGB_U8:    zmax = 255; break;
      case IMAGE_RGB_U16:   zmax = 65535; break;
      default:              NOT_IMPL_IMAGE_TYPE(type); return;
    }
    
    /*  Create the response functions */
    SimulResponse *R, *G, *B;
    switch (resp_shape)
    {
    case RESP_LINEAR:
        R = new SimulResponseLinear (param_resp_lin_.Xmin_R, param_resp_lin_.Xmax_R, zmax);
        G = new SimulResponseLinear (param_resp_lin_.Xmin_G, param_resp_lin_.Xmax_G, zmax);
        B = new SimulResponseLinear (param_resp_lin_.Xmin_B, param_resp_lin_.Xmax_B, zmax);
        break;
    case RESP_HALFCOS:
        R = new SimulResponseHalfCos (param_resp_cos_.Xmin_R, param_resp_cos_.Xmax_R, zmax);
        G = new SimulResponseHalfCos (param_resp_cos_.Xmin_G, param_resp_cos_.Xmax_G, zmax);
        B = new SimulResponseHalfCos (param_resp_cos_.Xmin_B, param_resp_cos_.Xmax_B, zmax);
        break;
    default:
        return;    
    }
    
    /*  Create the radiance scene */
    RadianceScene* scene = 0;
    const char* basename = 0;
    bool before = false;  // SCENE_RANDOM needs true
    switch (scene_shape)
    {
    case SCENE_SLOPE_XY:
        scene = new RadianceSceneSlopeXY (xdim, ydim, 0.0, 1.0);
        basename = "SlopeXY";
        break;
    case SCENE_RANDOM:    
        scene = new RadianceSceneRandom (xdim, ydim, 0.0, 1.0);
        basename = "Random";
        before = true;
        break;
    }
    
    /*  Create the simulated images and add them to the manager */
    m_Br2Hdr.beginAddImages();
    switch (type)
    {
    case IMAGE_RGB_U8:  
        add_simul_images_RGB_U8 (m_Br2Hdr,  xdim,ydim, N, 
                                 *scene, *R,*G,*B,  basename, before);
        break;
    case IMAGE_RGB_U16: 
        add_simul_images_RGB_U16(m_Br2Hdr,  xdim,ydim, N,
                                 *scene, *R,*G,*B,  basename, before);
        break;
    default:            
        NOT_IMPL_IMAGE_TYPE(type); 
        break;
    }
    
    delete R; delete G; delete B;
    delete scene;
    
    m_Br2Hdr.timesSetting (Br2Hdr::BY_NONE);  // use our times!
    m_Br2Hdr.endAddImages();
}



// END OF FILE
