//<copyright>
// 
// Copyright (c) 1993,94,95,96
// Institute for Information Processing and Computer Supported New Media (IICM),
// Graz University of Technology, Austria.
// 
//</copyright>

//<file>
//
// Name:        themenus.C
//
// Purpose:     implementation of menu and dialog classes for the scene viewer
//
// Created:     17 Dec 92   Michael Pichler
//
// Changed:     23 Jan 96   Michael Pichler
//
// $Id: themenus.C,v 1.21 1996/01/30 11:24:43 mpichler Exp $
//
//</file>



#include "themenus.h"

#include "camera.h"
#include "gecontext.h"
#include "hgptrhand.h"
#include "vecutil.h"
#include "stranslate.h"
#include "geomobj.h"
#include "scenewin.h"

#include <hyperg/widgets/coldlg.h>
#include <hyperg/widgets/cursors.h>
#include <hyperg/widgets/fchooser.h>
#include <hyperg/widgets/fieldb.h>
#include <hyperg/widgets/glyphutil.h>
#include <hyperg/widgets/menus.h>
#include <hyperg/widgets/msgbox.h>
#include <hyperg/widgets/textb.h>

#include <ge3d/ge3d.h>

#include <hyperg/hyperg/verbose.h>

#include <InterViews/background.h>
#include <InterViews/label.h>
#include <InterViews/layout.h>
#include <InterViews/patch.h>
#include <InterViews/style.h>
#include <IV-look/kit.h>
#include <IV-look/menu.h>
#include <OS/string.h>

#include <string.h>
#include <iostream.h>
#include <fstream.h>

/* access */
#ifdef PMAX
#  include <sysent.h>
#endif
#include <unistd.h>

declareActionCallback (SceneMenus)
implementActionCallback (SceneMenus)

declareActionCallback (Scene3D)
implementActionCallback (Scene3D)

declareActionCallback (SceneWindow)
implementActionCallback (SceneWindow)



/*** MenuGroupCallback ***/


MenuGroupCallback::MenuGroupCallback (SceneMenus* sm, SceneMenuMemberFunction fu, int arg)
{
  sm_ = sm;
  fu_ = fu;
  arg_ = arg;
}


void MenuGroupCallback::execute ()
{
  (sm_->*fu_) (arg_);
}


/*** LoadDemoCallback ***/
// action for loading a demo

class LoadDemoCallback: public Action
{
  public:
    LoadDemoCallback (
      SceneMenus*,
      const char* demo  // assumed to point to static string (not copied!)
    );

    virtual void execute ();

  private:
    SceneMenus* sm_;
    const char* demo_;
};


LoadDemoCallback::LoadDemoCallback (SceneMenus* sm, const char* demo)
{
  sm_ = sm;
  demo_ = demo;
}


void LoadDemoCallback::execute ()
{
  sm_->loadDemo (demo_);
}


/*** BrowseDemoCallback ***/
// action for loading and browsing a demo (camera positioning)

class BrowseDemoCallback: public Action
{
  public:
    BrowseDemoCallback (
      SceneMenus*,
      const char* demo,  // see note of LoadDemoCallback
      float px, float py, float pz,  // camera position
      float lx, float ly, float lz   // lookat point
    );

    virtual void execute ();

  private:
    SceneMenus* sm_;
    const char* demo_;
    point3D position_, lookat_;
};


BrowseDemoCallback::BrowseDemoCallback (
  SceneMenus* sm, const char* demo,
  float px, float py, float pz,
  float lx, float ly, float lz
)
{
  sm_ = sm;
  demo_ = demo;
  init3D (position_, px, py, pz);
  init3D (lookat_, lx, ly, lz);
}


void BrowseDemoCallback::execute ()
{
  sm_->browseDemo (demo_, position_, lookat_);
}


/*** SetColourCallback ***/
// action for setting a predefined colour

class SetColourCallback: public Action
{
  public:
    SetColourCallback (
      SceneMenus*,
      colorRGB* color,
      float r, float g, float b,
      int needsrebuild = 0
    );

    virtual void execute ();

  private:
    SceneMenus* sm_;
    colorRGB* color_;
    colorRGB predef_;
    int rebuild_;
};


SetColourCallback::SetColourCallback (
  SceneMenus* sm, colorRGB* color, float r, float g, float b, int needsrebuild
)
{
  sm_ = sm;
  color_ = color;
  initRGB (predef_, r, g, b);
  rebuild_ = needsrebuild;
}


void SetColourCallback::execute ()
{
  *color_ = predef_;
  if (rebuild_)
    sm_->scene_->colorRebuild ();
  sm_->colourChanged ();
}


/*** ViewpointCallback ***/
// action for predefined viewpoints

class ViewpointCallback: public Action
{
  public:
    ViewpointCallback (
      SceneMenus* sm, const char* name, QvPerspectiveCamera* pcam, QvOrthographicCamera* ocam
    )
    { sm_ = sm;  name_ = name;  pcam_ = pcam;  ocam_ = ocam;
    }

    virtual void execute ();

  private:
    SceneMenus* sm_;
    RString name_;
    QvPerspectiveCamera* pcam_;
    QvOrthographicCamera* ocam_;
};


void ViewpointCallback::execute ()
{
  SceneWindow* scene = sm_->scene_;
  GEContext* gecontext = scene->gecontext ();

  if (gecontext)
    gecontext->pushCursor (HgCursors::instance ()->hourglass ());

  scene->activateCamera (name_, pcam_, ocam_);  // includes a rebuild

  if (gecontext)
    gecontext->popCursor ();

  sm_->inputhand_->reset ();
  scene->redraw ();
}



/*** SceneMenus ***/


SceneMenus::SceneMenus (
  Patch* menupatch, Patch* buttonpatch, SceneWindow* scene,
  GEContext* gecontext, HG3dInputHandler* inputhand,
  WidgetKit* kit, const LayoutKit* layout
)
{
  menupatch_ = menupatch;
  buttonpatch_ = buttonpatch;
  scene_ = scene;
  gecontext_ = gecontext;
  inputhand_ = inputhand;
  kit_ = kit;
  layout_ = layout;
  openfc_ = 0;
//savefc_ = 0;
  coldlg_ = 0;
  errorwindow_ = 0;
  errorbrowser_ = 0;
  navsetdlg_ = 0;
  sballwindow_ = 0;

  viewpointsPr_ = 0;

  const int enabled_toggle = TelltaleState::is_enabled | TelltaleState::is_toggle;
  const int enabled_choosable = TelltaleState::is_enabled | TelltaleState::is_choosable;
#ifdef SPACEBALL
  const int enabled_toggle_choosen = enabled_toggle | TelltaleState::is_chosen;
#endif

  saveasstate_ = 0;  // new TelltaleState (0);  // disabled until scene to save (not used)
  exportSDFstate_ = new TelltaleState (0);  // disabled until scene to export
  exportVRMLstate_ = new TelltaleState (0);  // disabled until scene to export
  Resource::ref (saveasstate_);
  Resource::ref (exportSDFstate_);
  Resource::ref (exportVRMLstate_);

  sourceanchorstate_ = new TelltaleState (0);  // disabled as long no object or group selected
  deleteanchorstate_ = new TelltaleState (0);  // disabled as long no anchor selected
  anchorinfostate_ = new TelltaleState (0);  // as deleteanchorstate
  activateanchorstate_ = new TelltaleState (0);  // as deleteanchorstate
  removeselectionstate_ = new TelltaleState (0);  // as sourceanchorstate
  Resource::ref (sourceanchorstate_);
  Resource::ref (deleteanchorstate_);
  Resource::ref (anchorinfostate_);
  Resource::ref (activateanchorstate_);
  Resource::ref (removeselectionstate_);

  anchormotionstate_ = new TelltaleState (enabled_toggle);
  velocitycontrolstate_ = new TelltaleState (enabled_toggle);
  arbitraryrotstate_ = new TelltaleState (enabled_toggle);
  colldetectionstate_ = new TelltaleState (enabled_toggle);
  showanchorstate_ = new TelltaleState (enabled_toggle);
  aaliasingstate_ = new TelltaleState (enabled_toggle);
  viewlgtstate_ = new TelltaleState (enabled_toggle);
  Resource::ref (anchormotionstate_);
  Resource::ref (velocitycontrolstate_);
  Resource::ref (arbitraryrotstate_);
  Resource::ref (colldetectionstate_);
  Resource::ref (showanchorstate_);
  Resource::ref (aaliasingstate_);
  Resource::ref (viewlgtstate_);

#ifdef SPACEBALL
  sbflagstate_ [sb_translate] = new TelltaleState (enabled_toggle_choosen);
  sbflagstate_ [sb_rotate] = new TelltaleState (enabled_toggle_choosen);
  sbflagstate_ [sb_saf] = new TelltaleState (enabled_toggle);
#endif

  int i;
  for (i = 0;  i < sb_numflags;  i++)
  {
#ifdef SPACEBALL
    Resource::ref (sbflagstate_ [i]);
#else
    sbflagstate_ [i] = nil;
#endif
  }

  sbsensfieldb_ = 0;
  sbsensitivity2_ = 0;

  // need telltale states for display modes to update menu on key accelerators;
  // join to group is done by the radioMenuItems
  wireframe_ = new TelltaleState (enabled_choosable);
  hiddenline_ = new TelltaleState (enabled_choosable);
  flatshading_ = new TelltaleState (enabled_choosable);
  smoothshading_ = new TelltaleState (enabled_choosable);
  texturing_ = new TelltaleState (enabled_choosable);
  Resource::ref (wireframe_);
  Resource::ref (hiddenline_);
  Resource::ref (flatshading_);
  Resource::ref (smoothshading_);
  Resource::ref (texturing_);

  // interactive drawing modes
  intsame_ = new TelltaleState (enabled_choosable);
  intwire_ = new TelltaleState (enabled_choosable);
  inthline_ = new TelltaleState (enabled_choosable);
  intflat_ = new TelltaleState (enabled_choosable);
  intsmooth_ = new TelltaleState (enabled_choosable);
  inttexture_ = new TelltaleState (enabled_choosable);

  navflipobjstate_ = new TelltaleState (enabled_choosable);
  navwalkstate_    = new TelltaleState (enabled_choosable);
  navflystate_     = new TelltaleState (enabled_choosable);
  navflytostate_   = new TelltaleState (enabled_choosable);
  navheadsupstate_ = new TelltaleState (enabled_choosable);
  Resource::ref (navflipobjstate_);
  Resource::ref (navwalkstate_);
  Resource::ref (navflystate_);
  Resource::ref (navflytostate_);
  Resource::ref (navheadsupstate_);

  String valstr;  // e.g. "../data"
  kit_->style ()->find_attribute ("demoPath", valstr);
  demopath_ = valstr.string ();
//kit_->style ()->find_attribute ("demoPath", demopath_);
//cerr << "loading demos from path " << demopath_ << endl;

  // initial settings (default modes)
  scene_->setNavigationMode (NavMode::flip_obj);  // navigation mode
  scene_->mode (ge3d_smooth_shading);  // display mode
  scene_->linksColor (Scene3D::l_diffcolor);  // anchor highlighting mode
} // SceneMenus


SceneMenus::~SceneMenus ()
{
  Resource::unref (saveasstate_);
  Resource::unref (exportSDFstate_);
  Resource::unref (exportVRMLstate_);

  Resource::unref (sourceanchorstate_);
  Resource::unref (deleteanchorstate_);
  Resource::unref (anchorinfostate_);
  Resource::unref (activateanchorstate_);
  Resource::unref (removeselectionstate_);

  Resource::unref (anchormotionstate_);
  Resource::unref (velocitycontrolstate_);
  Resource::unref (arbitraryrotstate_);
  Resource::unref (colldetectionstate_);
  Resource::unref (showanchorstate_);
  Resource::unref (aaliasingstate_);
  Resource::unref (viewlgtstate_);

#ifdef SPACEBALL
  int i;
  for (i = 0;  i < sb_numflags;  i++)
    Resource::unref (sbflagstate_ [i]);
#endif
  Resource::unref (sbsensfieldb_);

  Resource::unref (wireframe_);
  Resource::unref (hiddenline_);
  Resource::unref (flatshading_);
  Resource::unref (smoothshading_);
  Resource::unref (texturing_);

  Resource::unref (intsame_);
  Resource::unref (intwire_);
  Resource::unref (inthline_);
  Resource::unref (intflat_);
  Resource::unref (intsmooth_);
  Resource::unref (inttexture_);

  Resource::unref (navflipobjstate_);
  Resource::unref (navwalkstate_);
  Resource::unref (navflystate_);
  Resource::unref (navflytostate_);
  Resource::unref (navheadsupstate_);

  Resource::unref (openfc_);
//Resource::unref (savefc_);
  delete coldlg_;

  delete errorwindow_;  // errorbrowser_ will be destroyed as part of it
  destroyNavsetdlg ();
  delete sballwindow_;

  Resource::unref (viewpointsPr_);

} // ~SceneMenus



// createMenus
// creates a menubar with all its submenus and
// puts it into the menupatch, which is redrawn

void SceneMenus::createMenus ()
{
  // right aligned help pulldown would be nice, but InterViews aligns
  // all submenus of the menubar same way (see Menu::open)
  // align () would be easy to override,
  // but place () has no access to allocation data

  WidgetKit& kit = *kit_;
  const LayoutKit& layout = *layout_;

  Menu* menubar = MenuKit::menubar (kit, layout);

  menubar->append_item (MenuKit::menubarItem (
    kit, STranslate::str (STranslate::MenuFILE), filePulldown (kit, layout)));
  menubar->append_item (MenuKit::menubarItem (
    kit, STranslate::str (STranslate::MenuNAVIGATE), navigatePulldown (kit, layout)));
  menubar->append_item (MenuKit::menubarItem (
    kit, STranslate::str (STranslate::MenuANCHORS), anchorsPulldown (kit, layout)));
  if (kit.style ()->value_is_on ("DocumentMenuItems"))
  { menubar->append_item (MenuKit::menubarItem (
      kit, STranslate::str (STranslate::MenuDOCUMENT), documentPulldown (kit, layout)));
  }
  menubar->append_item (MenuKit::menubarItem (
    kit, STranslate::str (STranslate::MenuVIEW), viewPulldown (kit, layout)));
//   menubar->append_item (MenuKit::menubarItem (
//     kit, STranslate::str (STranslate::MenuOPTIONS), optionsPulldown (kit, layout)));
  menubar->append_item (MenuKit::menubarGlue (layout));
  menubar->append_item (MenuKit::menubarItem (
    kit, STranslate::str (STranslate::MenuHELP), helpPulldown (kit, layout)));

  menupatch_->body (menubar);
  menupatch_->reallocate ();
  menupatch_->redraw ();

} // createMenus



// createButtons
// create button tool bar

void SceneMenus::createButtons ()
{
  // WidgetKit& kit = *kit_;
  const LayoutKit& layout = *layout_;

  // currently no flip object submode buttons
  Glyph* flipbutton = WWidgetKit::palette_button (STranslate::str (STranslate::NavFLIPOBJECT),
    new MenuGroupCallback (this, &SceneMenus::movementMode, NavMode::flip_obj), navflipobjstate_);
  Glyph* walkbutton = WWidgetKit::palette_button (STranslate::str (STranslate::NavWALK),
    new MenuGroupCallback (this, &SceneMenus::movementMode, NavMode::walk_around), navwalkstate_);
  Glyph* flybutton  = WWidgetKit::palette_button (STranslate::str (STranslate::NavFLY),
    new MenuGroupCallback (this, &SceneMenus::movementMode, NavMode::fly_1), navflystate_);
  Glyph* fly2button = WWidgetKit::palette_button (STranslate::str (STranslate::NavFLYTO),
    new MenuGroupCallback (this, &SceneMenus::movementMode, NavMode::fly_2), navflytostate_);
  Glyph* hupbutton  = WWidgetKit::palette_button (STranslate::str (STranslate::NavHEADSUP),
    new MenuGroupCallback (this, &SceneMenus::movementMode, NavMode::heads_up), navheadsupstate_);
  Glyph* anchbutton = WWidgetKit::palette_button (STranslate::str (STranslate::ButtonSHOWANCHORS), 
    new ActionCallback(SceneMenus) (this, &SceneMenus::showAnchorsChanged), showanchorstate_);

  Glyph* toolbox = layout.hbox (
    flipbutton,
    layout.hspace (5),
    walkbutton,
    flybutton,
    fly2button,
    hupbutton,
    layout.hglue (10, fil, 0),
    anchbutton
  );

  buttonpatch_->body (toolbox);
  buttonpatch_->reallocate ();
  buttonpatch_->redraw ();

} // createButtons


void SceneMenus::updateDialogs ()
{
  updateAbout (*kit_, *layout_);

  // update file choosers
  if (openfc_)
    buildOpenFC ();

// if (savefc_) ...

  if (coldlg_)
  {
    int mapped = coldlg_->ismapped ();
    buildColourDialog ();  // deletes and rebuilds colour dialog
    if (mapped)
      coldlg_->map ();
  }

} // updateDialogs


void SceneMenus::selectionChanged ()
{
  // TODO: VRML anchors
  GeometricObject* selobj = scene_->selectedObj ();
  int curanchor = selobj && selobj->getCurrentAnchor ();

  sourceanchorstate_->set (TelltaleState::is_enabled, selobj ? 1 : 0);
  deleteanchorstate_->set (TelltaleState::is_enabled, curanchor);
  anchorinfostate_->set (TelltaleState::is_enabled, curanchor);
  activateanchorstate_->set (TelltaleState::is_enabled, curanchor);
  removeselectionstate_->set (TelltaleState::is_enabled, selobj ? 1 : 0);

} // selectionChanged


void SceneMenus::sceneChanged ()
{
//existance of a input file not known at the moment this function is called
//saveasstate_->set (TelltaleState::is_enabled, scene_->supportsOutputformat (Scene3D::write_ORIGINAL));
  exportSDFstate_->set (TelltaleState::is_enabled, scene_->supportsOutputformat (Scene3D::write_SDF));
  exportVRMLstate_->set (TelltaleState::is_enabled, scene_->supportsOutputformat (Scene3D::write_VRML));

} // sceneChanged


Menu* SceneMenus::filePulldown (WidgetKit& kit, const LayoutKit& layout)
{
  Menu* file_pd = kit.pulldown ();

  file_pd->append_item (MenuKit::menuItem (
    kit, layout, STranslate::str (STranslate::FileOPEN),
    new ActionCallback(SceneMenus) (this, &SceneMenus::openFile), "F3"));

  if (kit.style ()->value_is_on ("DemoMenuItems"))
  {
    file_pd->append_item (MenuKit::menuItem (
      kit, layout, STranslate::str (STranslate::FileDEMO), demoPullright (kit, layout)));
    file_pd->append_item (MenuKit::menuItem (
      kit, layout, STranslate::str (STranslate::FileUSABILITYTESTS), usabilityPullright (kit, layout)));
  } // demo items

  Menu* export_pr = kit.pullright ();  // save as suboptions (should be integrated into save dialog)
  export_pr->append_item (MenuKit::menuItem (kit, layout, "VRML",
                          new ActionCallback(SceneMenus) (this, &SceneMenus::saveVRMLScene), 0, exportVRMLstate_));
  export_pr->append_item (MenuKit::menuItem (kit, layout, "SDF",
                          new ActionCallback(SceneMenus) (this, &SceneMenus::saveSDFScene), 0, exportSDFstate_));

  // File Menu - common parts
  file_pd->append_item (MenuKit::menuItem (kit, layout, STranslate::str (STranslate::FileSAVEAS),
                        new ActionCallback(SceneMenus) (this, &SceneMenus::saveOriginalScene), "F2", saveasstate_));
  file_pd->append_item (MenuKit::menuItem (kit, layout, STranslate::str (STranslate::FileEXPORT),
                        export_pr));
  file_pd->append_item (MenuKit::disable (MenuKit::menuItem (
    kit, layout, STranslate::str (STranslate::FileEDIT), (Action*) nil)));
  file_pd->append_item (MenuKit::disable (MenuKit::menuItem (
    kit, layout, STranslate::str (STranslate::FilePRINT), (Action*) nil)));
  file_pd->append_item (MenuKit::disable (MenuKit::menuItem (
    kit, layout, STranslate::str (STranslate::FileMAIL), (Action*) nil)));
  file_pd->append_item (MenuKit::menuItem (
    kit, layout, STranslate::str (STranslate::FilePARSERERRORS),
    new ActionCallback(SceneMenus) (this, &SceneMenus::toggleErrorDialog)));

  if (kit.style ()->value_is_on ("SpecialMenuItems"))
  {
    file_pd->append_item (kit.menu_item_separator ());

    file_pd->append_item (MenuKit::menuItem (kit, layout, STranslate::str (STranslate::SpecialPRINTDATA),
                          new ActionCallback(SceneMenus) (this, &SceneMenus::printData)));
    file_pd->append_item (MenuKit::menuItem (kit, layout, STranslate::str (STranslate::SpecialPRINTALL),
                          new ActionCallback(SceneMenus) (this, &SceneMenus::printAll)));
    file_pd->append_item (MenuKit::menuItem (kit, layout, STranslate::str (STranslate::SpecialCAMERAINFO),
                          new ActionCallback(SceneMenus) (this, &SceneMenus::cameraInfo)));
#ifdef TODO  /* saveDepth */
    // will be integrated into FileSAVEAS !!!
    file_pd->append_item (MenuKit::menuItem (kit, layout, "save depth file",
                          new ActionCallback(SceneMenus) (this, &SceneMenus::saveDepth)));
#endif
  } // special items


  file_pd->append_item (kit.menu_item_separator ());
  file_pd->append_item (MenuKit::menuItem (
    kit, layout, STranslate::str (STranslate::FileEXITVIEWER), kit.quit (), "^x"));

  return file_pd;

} // filePulldown


Menu* SceneMenus::demoPullright (WidgetKit& kit, const LayoutKit& layout)
{
  Menu* demo_pr = kit.pullright ();

  // old file format (SDF); will go away soon
  demo_pr->append_item (MenuKit::menuItem (kit, layout, STranslate::str (STranslate::DemoCUBE),
                        new LoadDemoCallback (this, "/cube/cube.sdf")));
  demo_pr->append_item (MenuKit::menuItem (kit, layout, STranslate::str (STranslate::DemoROOM),
                        new LoadDemoCallback (this, "/room/room.sdf")));
  demo_pr->append_item (MenuKit::menuItem (kit, layout, STranslate::str (STranslate::DemoCAR_VW),
                        new LoadDemoCallback (this, "/car/car.sdf")));
  demo_pr->append_item (MenuKit::menuItem (kit, layout, STranslate::str (STranslate::DemoCORVETTE),
                        new LoadDemoCallback (this, "/corvette/car.sdf")));
  demo_pr->append_item (MenuKit::menuItem (kit, layout, STranslate::str (STranslate::DemoENGINE),
                        new LoadDemoCallback (this, "/engine/engine.sdf")));
  demo_pr->append_item (MenuKit::menuItem (kit, layout, STranslate::str (STranslate::DemoCLOCKTOWER),
                        new LoadDemoCallback (this, "/uhrturm/uhrturm.sdf")));
  demo_pr->append_item (MenuKit::menuItem (kit, layout, STranslate::str (STranslate::DemoWORLD),
                        new LoadDemoCallback (this, "/world/world.sdf")));
  demo_pr->append_item (MenuKit::menuItem (kit, layout, STranslate::str (STranslate::DemoIICMINST),
                        new LoadDemoCallback (this, "/inst/inst.sdf")));
  demo_pr->append_item (MenuKit::menuItem (kit, layout, STranslate::str (STranslate::DemoIICMFOYER),
                        new BrowseDemoCallback (this, "/inst/inst.sdf", 10, 2, 22, 10, 2, 21)));
  demo_pr->append_item (MenuKit::menuItem (kit, layout, STranslate::str (STranslate::DemoKANDHLEIT),
                        new LoadDemoCallback (this, "/inst/ka_hl.sdf")));


  return demo_pr;

} // demoPullright


Menu* SceneMenus::usabilityPullright (WidgetKit& kit, const LayoutKit& layout)
{
  Menu* ut_pr = kit.pullright ();

  ut_pr->append_item (MenuKit::menuItem (kit, layout, STranslate::str (STranslate::UTestNORTH),
                      new BrowseDemoCallback (this, "/ihci/ihci.sdf", 6, 1.8, -10, 6, 1.8, -9)));
  ut_pr->append_item (MenuKit::menuItem (kit, layout, STranslate::str (STranslate::UTestWEST),
                      new BrowseDemoCallback (this, "/ihci/ihci.sdf", -8, 1.8, 5, -7, 1.8, 5)));
  ut_pr->append_item (MenuKit::menuItem (kit, layout, STranslate::str (STranslate::UTestSOUTH),
                      new BrowseDemoCallback (this, "/ihci/ihci.sdf", 6, 1.8, 20, 6, 1.8, 19)));
  ut_pr->append_item (MenuKit::menuItem (kit, layout, STranslate::str (STranslate::UTestEAST),
                      new BrowseDemoCallback (this, "/ihci/ihci.sdf", 20, 1.8, 5, 19, 1.8, 5)));

  return ut_pr;

} // usabilityPullright



Menu* SceneMenus::navigatePulldown (WidgetKit& kit, const LayoutKit& layout)
{
  Menu* navigate_pd = kit.pulldown ();

  // Movement modes
  static TelltaleGroup* mmgroup = 0;
  if (!mmgroup)
    mmgroup = new TelltaleGroup ();
  const int mmode = scene_->navigationMode ();

  navigate_pd->append_item (MenuKit::radioMenuItem (
    kit, layout, mmgroup, STranslate::str (STranslate::NavFLIPOBJECT),
    new MenuGroupCallback (this, &SceneMenus::movementMode, NavMode::flip_obj),
    mmode == NavMode::flip_obj, "F4", navflipobjstate_));
  navigate_pd->append_item (MenuKit::radioMenuItem (
    kit, layout, mmgroup, STranslate::str (STranslate::NavWALK),
    new MenuGroupCallback (this, &SceneMenus::movementMode, NavMode::walk_around),
    mmode == NavMode::walk_around, "F5", navwalkstate_));
  navigate_pd->append_item (MenuKit::radioMenuItem (
    kit, layout, mmgroup, STranslate::str (STranslate::NavFLY),
    new MenuGroupCallback (this, &SceneMenus::movementMode, NavMode::fly_1),
    mmode == NavMode::fly_1, "F6", navflystate_));
  navigate_pd->append_item (MenuKit::radioMenuItem (
    kit, layout, mmgroup, STranslate::str (STranslate::NavFLYTO),
    new MenuGroupCallback (this, &SceneMenus::movementMode, NavMode::fly_2),
    mmode == NavMode::fly_2, "F7", navflytostate_));
  navigate_pd->append_item (MenuKit::radioMenuItem (
    kit, layout, mmgroup, STranslate::str (STranslate::NavHEADSUP),
    new MenuGroupCallback (this, &SceneMenus::movementMode, NavMode::heads_up),
    mmode == NavMode::heads_up, "F8", navheadsupstate_));

  navigate_pd->append_item (kit.menu_item_separator ());

  // navigation options pullright
  Menu* navopt_pr = kit.pullright ();
  navopt_pr->append_item (MenuKit::checkMenuItem (
    kit, layout, STranslate::str (STranslate::OptionENABLEMOTION),
    new ActionCallback(SceneMenus) (this, &SceneMenus::anchorMotionChanged),
    scene_->anchorMotion (), 0, anchormotionstate_));
  navopt_pr->append_item (MenuKit::checkMenuItem (
    kit, layout, STranslate::str (STranslate::OptionVELOCITYCONTROL),
    new ActionCallback(SceneMenus) (this, &SceneMenus::velocityControlChanged),
    scene_->velocityControl (), 0, velocitycontrolstate_));
  navopt_pr->append_item (MenuKit::checkMenuItem (
    kit, layout, STranslate::str (STranslate::OptionARBITRARYROTATIONS),
    new ActionCallback(SceneMenus) (this, &SceneMenus::arbitraryRotationChanged),
    scene_->arbitraryRotations (), 0, arbitraryrotstate_));
  navopt_pr->append_item (MenuKit::checkMenuItem (
    kit, layout, STranslate::str (STranslate::OptionCOLLISIONDETECTION),
    new ActionCallback(SceneMenus) (this, &SceneMenus::collisionDetectionChanged),
    scene_->collisionDetection (), 0, colldetectionstate_));

  Resource::unref (viewpointsPr_);
  viewpointsPr_ = kit.pulldown ();
  Resource::ref (viewpointsPr_);

  // viewpoints, reset view, level view
  navigate_pd->append_item (MenuKit::menuItem (
    kit, layout, STranslate::str (STranslate::MenuVIEWPOINTS), viewpointsPr_));
  navigate_pd->append_item (MenuKit::menuItem (
    kit, layout, STranslate::str (STranslate::NavigateRESETVIEW),
    new ActionCallback(SceneMenus) (this, &SceneMenus::resetView), "r"));
  navigate_pd->append_item (MenuKit::menuItem (
    kit, layout, STranslate::str (STranslate::NavigateLEVELVIEW),
    new ActionCallback(SceneMenus) (this, &SceneMenus::levelView), "l"));
  navigate_pd->append_item (MenuKit::menuItem (
    kit, layout, STranslate::str (STranslate::NavigateUNTILTVIEW),
    new ActionCallback(SceneMenus) (this, &SceneMenus::untiltView), "u"));

  navigate_pd->append_item (kit.menu_item_separator ());

  // options, settings, spaceball
  navigate_pd->append_item (MenuKit::menuItem (
    kit, layout, STranslate::str (STranslate::MenuOPTIONS), navopt_pr));
  navigate_pd->append_item (MenuKit::menuItem (
    kit, layout, STranslate::str (STranslate::NavigateSETTINGS),
    new ActionCallback(SceneMenus) (this, &SceneMenus::toggleNavigationSettingsDialog)));
  navigate_pd->append_item (
#ifndef SPACEBALL
    MenuKit::disable (
#endif
      MenuKit::menuItem (
        kit, layout, STranslate::str (STranslate::NavigateSPACEBALL),
        new ActionCallback(SceneMenus) (this, &SceneMenus::toggleSpaceballDialog))
#ifndef SPACEBALL
    )
#endif
  );

  navigate_pd->append_item (kit.menu_item_separator ());

  // common to all viewers: back, forward, history; hold
  navigate_pd->append_item (MenuKit::menuItem (
    kit, layout, STranslate::str (STranslate::NavigateBACK),
    new ActionCallback(Scene3D) (scene_, &Scene3D::back), "b"));
  navigate_pd->append_item (MenuKit::menuItem (
    kit, layout, STranslate::str (STranslate::NavigateFORWARD),
    new ActionCallback(Scene3D) (scene_, &Scene3D::forward), "f"));
  navigate_pd->append_item (MenuKit::menuItem (
    kit, layout, STranslate::str (STranslate::NavigateHISTORY),
    new ActionCallback(Scene3D) (scene_, &Scene3D::history), "h"));
  navigate_pd->append_item (kit.menu_item_separator ());
  navigate_pd->append_item (MenuKit::menuItem (
    kit, layout, STranslate::str (STranslate::NavigateHOLD),
    new ActionCallback(Scene3D) (scene_, &Scene3D::hold), "H"));

  return navigate_pd;

} // navigatePulldown



Menu* SceneMenus::anchorsPulldown (WidgetKit& kit, const LayoutKit& layout)
{
  Menu* anchors_pd = kit.pulldown ();

  anchors_pd->append_item (MenuKit::menuItem (
    kit, layout, STranslate::str (STranslate::DocumentATTRIBUTES),
    new ActionCallback(Scene3D) (scene_, &Scene3D::anchorinfo), 0, anchorinfostate_));
  // next Anchor not yet implemented
//   anchors_pd->append_item (MenuKit::menuItem (
//     kit, layout, STranslate::str (STranslate::AnchorNEXT),
//     [Action], "Tab", [state]));
  anchors_pd->append_item (MenuKit::menuItem (
    kit, layout, STranslate::str (STranslate::AnchorFOLLOW),
    new ActionCallback(SceneWindow) (scene_, &SceneWindow::activateAnchor), "Ret", activateanchorstate_));

  anchors_pd->append_item (kit.menu_item_separator ());

  // definition of source and destination anchors
  anchors_pd->append_item (MenuKit::menuItem (
    kit, layout, STranslate::str (STranslate::AnchorDEFINESOURCE),
    new ActionCallback(Scene3D) (scene_, &Scene3D::setSourceAnchor), 0, sourceanchorstate_));
  anchors_pd->append_item (MenuKit::menuItem (
    kit, layout, STranslate::str (STranslate::AnchorDEFINEDESTINATION),
    new ActionCallback(Scene3D) (scene_, &Scene3D::setDestinationAnchor)));
  anchors_pd->append_item (MenuKit::menuItem (
    kit, layout, STranslate::str (STranslate::AnchorDEFAULTDESTINATION),
    new ActionCallback(Scene3D) (scene_, &Scene3D::setDefDestAnchor)));

  anchors_pd->append_item (kit.menu_item_separator ());

  anchors_pd->append_item (MenuKit::menuItem (
    kit, layout, STranslate::str (STranslate::AnchorDELETE),
    new ActionCallback(Scene3D) (scene_, &Scene3D::do_deleteSourceAnchor),
    "Del", deleteanchorstate_));

  anchors_pd->append_item (kit.menu_item_separator ());

  // selection of objects/groups
  static TelltaleGroup* osgroup = 0;
  if (!osgroup)
    osgroup = new TelltaleGroup ();

  int selmode = scene_->selectionMode ();
  anchors_pd->append_item (MenuKit::radioMenuItem (
    kit, layout, osgroup, STranslate::str (STranslate::AnchorSELECTOBJECTS),
    new MenuGroupCallback (this, &SceneMenus::selectionMode, SceneWindow::SelectObject),
    selmode == SceneWindow::SelectObject));
  anchors_pd->append_item (MenuKit::radioMenuItem (
    kit, layout, osgroup, STranslate::str (STranslate::AnchorSELECTGROUPS),
    new MenuGroupCallback (this, &SceneMenus::selectionMode, SceneWindow::SelectGroup),
    selmode == SceneWindow::SelectGroup));
//   anchors_pd->append_item (MenuKit::radioMenuItem (
//     kit, layout, osgroup, STranslate::str (STranslate::AnchorSELECTANCHORS),
//     new MenuGroupCallback (this, &SceneMenus::selectionMode, SceneWindow::SelectAnchor),
//     selmode == SceneWindow::SelectAnchor));
  anchors_pd->append_item (MenuKit::menuItem (
    kit, layout, STranslate::str (STranslate::AnchorDESELECT),
    new ActionCallback(SceneMenus) (this, &SceneMenus::removeSelection), 0, removeselectionstate_));

  return anchors_pd;

} // anchorsPulldown



Menu* SceneMenus::viewPulldown (WidgetKit& kit, const LayoutKit& layout)
{
  Menu* view_pd = kit.pulldown ();

  // display/drawing options (smooth shading etc.)
  static TelltaleGroup* dmgroup = 0;
  if (!dmgroup)
    dmgroup = new TelltaleGroup ();
  const int dmode = scene_->mode ();

  view_pd->append_item (MenuKit::radioMenuItem (
    kit, layout, dmgroup, STranslate::str (STranslate::DisplayWIREFRAME),
    new MenuGroupCallback (this, &SceneMenus::setDisplayMode, ge3d_wireframe),
    dmode == ge3d_wireframe, "^w", wireframe_));
  view_pd->append_item (MenuKit::radioMenuItem (
    kit, layout, dmgroup, STranslate::str (STranslate::DisplayHIDDENLINE),
    new MenuGroupCallback (this, &SceneMenus::setDisplayMode, ge3d_hidden_line),
    dmode == ge3d_hidden_line, "^h", hiddenline_));
  view_pd->append_item (MenuKit::radioMenuItem (
    kit, layout, dmgroup, STranslate::str (STranslate::DisplayFLATSHADING),
    new MenuGroupCallback (this, &SceneMenus::setDisplayMode, ge3d_flat_shading),
    dmode == ge3d_flat_shading, "^f", flatshading_));
  view_pd->append_item (MenuKit::radioMenuItem (
    kit, layout, dmgroup, STranslate::str (STranslate::DisplaySMOOTHSHADING),
    new MenuGroupCallback (this, &SceneMenus::setDisplayMode, ge3d_smooth_shading),
    dmode == ge3d_smooth_shading, "^s", smoothshading_));
  if (ge3dTexturingSupport ())  // does not require an open window
    view_pd->append_item (MenuKit::radioMenuItem (
      kit, layout, dmgroup, STranslate::str (STranslate::DisplayTEXTURING),
      new MenuGroupCallback (this, &SceneMenus::setDisplayMode, ge3d_texturing),
      dmode == ge3d_texturing, "^t", texturing_));

  // interactive display/drawing options
  static TelltaleGroup* imgroup = 0;
  if (!imgroup)
    imgroup = new TelltaleGroup ();
  const int imode = scene_->modeInteract ();

  Menu* iview_pr = kit.pullright ();

  iview_pr->append_item (MenuKit::radioMenuItem (
    kit, layout, imgroup, STranslate::str (STranslate::DisplayTHESAME),
    new MenuGroupCallback (this, &SceneMenus::setInteractiveMode, Scene3D::same_mode),
    imode == Scene3D::same_mode, "^u", intsame_));
  iview_pr->append_item (kit.menu_item_separator ());
  iview_pr->append_item (MenuKit::radioMenuItem (
    kit, layout, imgroup, STranslate::str (STranslate::DisplayWIREFRAME),
    new MenuGroupCallback (this, &SceneMenus::setInteractiveMode, ge3d_wireframe),
    imode == ge3d_wireframe, "^W", intwire_));
  iview_pr->append_item (MenuKit::radioMenuItem (
    kit, layout, imgroup, STranslate::str (STranslate::DisplayHIDDENLINE),
    new MenuGroupCallback (this, &SceneMenus::setInteractiveMode, ge3d_hidden_line),
    imode == ge3d_hidden_line, "^H", inthline_));
  iview_pr->append_item (MenuKit::radioMenuItem (
    kit, layout, imgroup, STranslate::str (STranslate::DisplayFLATSHADING),
    new MenuGroupCallback (this, &SceneMenus::setInteractiveMode, ge3d_flat_shading),
    imode == ge3d_flat_shading, "^F", intflat_));
  iview_pr->append_item (MenuKit::radioMenuItem (
    kit, layout, imgroup, STranslate::str (STranslate::DisplaySMOOTHSHADING),
    new MenuGroupCallback (this, &SceneMenus::setInteractiveMode, ge3d_smooth_shading),
    imode == ge3d_smooth_shading, "^S", intsmooth_));
  iview_pr->append_item (MenuKit::radioMenuItem (
    kit, layout, imgroup, STranslate::str (STranslate::DisplayTEXTURING),
    new MenuGroupCallback (this, &SceneMenus::setInteractiveMode, ge3d_texturing),
    imode == ge3d_texturing, 0, inttexture_));

  view_pd->append_item (kit.menu_item_separator ());
  view_pd->append_item (MenuKit::menuItem (
    kit, layout, STranslate::str (STranslate::DisplayINTERACTIVE), iview_pr));

  // twosided polygons pullright
  Menu* twoside_pr = kit.pullright ();
  {
    static TelltaleGroup* tsgroup = 0;
    if (!tsgroup)
      tsgroup = new TelltaleGroup ();
    const int tpolys = scene_->twosidedpolys ();

    twoside_pr->append_item (MenuKit::radioMenuItem (
      kit, layout, tsgroup, STranslate::str (STranslate::TwosidedON),
      new MenuGroupCallback (this, &SceneMenus::twosidedPolys, Scene3D::twosided_always),
      tpolys == Scene3D::twosided_always));
    twoside_pr->append_item (MenuKit::radioMenuItem (
      kit, layout, tsgroup, STranslate::str (STranslate::TwosidedAUTO),
      new MenuGroupCallback (this, &SceneMenus::twosidedPolys, Scene3D::twosided_auto),
      tpolys == Scene3D::twosided_auto));
    twoside_pr->append_item (MenuKit::radioMenuItem (
      kit, layout, tsgroup, STranslate::str (STranslate::TwosidedOFF),
      new MenuGroupCallback (this, &SceneMenus::twosidedPolys, Scene3D::twosided_never),
      tpolys == Scene3D::twosided_never));
  }

  // lighting calculations pullright
  Menu* lightcalc_pr = kit.pullright ();
  {
    static TelltaleGroup* lcgroup = 0;
    if (!lcgroup)
      lcgroup = new TelltaleGroup ();
    const int lighting = scene_->dolighting ();

    lightcalc_pr->append_item (MenuKit::radioMenuItem (
      kit, layout, lcgroup, STranslate::str (STranslate::TwosidedON),
      new MenuGroupCallback (this, &SceneMenus::doLighting, Scene3D::lighting_always),
      lighting == Scene3D::lighting_always));
    lightcalc_pr->append_item (MenuKit::radioMenuItem (
      kit, layout, lcgroup, STranslate::str (STranslate::TwosidedAUTO),
      new MenuGroupCallback (this, &SceneMenus::doLighting, Scene3D::lighting_auto),
      lighting == Scene3D::lighting_auto));
    lightcalc_pr->append_item (MenuKit::radioMenuItem (
      kit, layout, lcgroup, STranslate::str (STranslate::TwosidedOFF),
      new MenuGroupCallback (this, &SceneMenus::doLighting, Scene3D::lighting_never),
      lighting == Scene3D::lighting_never));

    lightcalc_pr->append_item (kit.menu_item_separator ());
    lightcalc_pr->append_item (MenuKit::checkMenuItem (
      kit, layout, STranslate::str (STranslate::OptionTEXTURELIGHTING),
      new ActionCallback(SceneMenus) (this, &SceneMenus::toggleTextureLighting),
        scene_->textureLighting ()));
  }

  view_pd->append_item (kit.menu_item_separator ());
  // anti-aliasing lines only; polygon anti-aliasing does not work together with Z-buffer
  // (would require oversampling in accumulation buffer or manual depth ordering)
  if (ge3dAntialiasingSupport ())  // does not require an open window
  {
    view_pd->append_item (MenuKit::checkMenuItem (
      kit, layout, STranslate::str (STranslate::DisplayAALIASING),
      new ActionCallback(SceneMenus) (this, &SceneMenus::aaliasingChanged),
      antialiasing (), "^a", aaliasingstate_));
  }
  view_pd->append_item (MenuKit::checkMenuItem (
    kit, layout, STranslate::str (STranslate::OptionSHOWFRAMERATE),
    new ActionCallback(SceneWindow) (scene_, &SceneWindow::toggleShowFramerate),
    scene_->showFramerate ()));
  view_pd->append_item (MenuKit::menuItem (
    kit, layout, STranslate::str (STranslate::OptionSHOWTITLE),
    new ActionCallback(SceneWindow) (scene_, &SceneWindow::showTitle)));
  view_pd->append_item (MenuKit::menuItem (
    kit, layout, STranslate::str (STranslate::OptionTWOSIDEDPOLYS), twoside_pr));
  view_pd->append_item (MenuKit::menuItem (
    kit, layout, STranslate::str (STranslate::OptionDOLIGHTING), lightcalc_pr));

  view_pd->append_item (MenuKit::checkMenuItem (
    kit, layout, STranslate::str (STranslate::ColorVIEWINGLIGHT),
    new ActionCallback(SceneMenus) (this, &SceneMenus::viewingLightChanged),
    scene_->viewingLight (), 0, viewlgtstate_));

  view_pd->append_item (kit.menu_item_separator ());

  view_pd->append_item (MenuKit::menuItem (
    kit, layout, STranslate::str (STranslate::MenuANCHORS), anchorsPullright (kit, layout)));

  view_pd->append_item (MenuKit::menuItem (
    kit, layout, STranslate::str (STranslate::MenuCOLOR), colorPullright (kit, layout)));

  return view_pd;

} // viewPulldown


Menu* SceneMenus::anchorsPullright (WidgetKit& kit, const LayoutKit& layout)
{
  Menu* anchors_pr = kit.pullright ();  // view/anchor pullright menu

  // anchor highlighting mode
  static TelltaleGroup* ahgroup = 0;
  if (!ahgroup)
    ahgroup = new TelltaleGroup ();
  const int ahmode = scene_->linksColor ();

  MenuItem* showanchors = MenuKit::checkMenuItem (
    kit, layout, STranslate::str (STranslate::AnchorDISPLAY),
    new ActionCallback(SceneMenus) (this, &SceneMenus::showAnchorsChanged),
    scene_->linksActive (), "F9", showanchorstate_);
  anchors_pr->append_item (showanchors);

  anchors_pr->append_item (kit.menu_item_separator ());

  anchors_pr->append_item (MenuKit::radioMenuItem (
    kit, layout, ahgroup, STranslate::str (STranslate::AnchBRIGHTNESS),
    new MenuGroupCallback (this, &SceneMenus::highlightingMode, Scene3D::l_brightness), ahmode == Scene3D::l_brightness));
//   anchors_pr->append_item (MenuKit::radioMenuItem (
//     kit, layout, ahgroup, STranslate::str (STranslate::AnchBOUNDINGCUBE),
//     new MenuGroupCallback (this, &SceneMenus::highlightingMode, Scene3D::l_boundingbox),
//     ahmode == Scene3D::l_boundingbox));
  anchors_pr->append_item (MenuKit::radioMenuItem (
    kit, layout, ahgroup, STranslate::str (STranslate::AnchCOLORCODE),
    new MenuGroupCallback (this, &SceneMenus::highlightingMode, Scene3D::l_diffcolor), ahmode == Scene3D::l_diffcolor));
  anchors_pr->append_item (MenuKit::radioMenuItem (
    kit, layout, ahgroup, STranslate::str (STranslate::AnchCOLOREDGES),
    new MenuGroupCallback (this, &SceneMenus::highlightingMode, Scene3D::l_coloredges), ahmode == Scene3D::l_coloredges));

  return anchors_pr;

} // anchorsPullright


Menu* SceneMenus::colorPullright (WidgetKit& kit, const LayoutKit& layout)
{
  // colour submenus (set predefined colour or select other colour)

  // background colour submenu
  Menu* backgcolor_pr = kit.pullright ();
  {
    colorRGB* backg = &scene_->col_background;
    backgcolor_pr->append_item (MenuKit::menuItem (
      kit, layout, STranslate::str (STranslate::ColBLACK),
      new SetColourCallback (this, backg, 0.0, 0.0, 0.0)));  // black
    backgcolor_pr->append_item (MenuKit::menuItem (
      kit, layout, STranslate::str (STranslate::ColGRAY),
      new SetColourCallback (this, backg, 0.5, 0.5, 0.5)));  // grey50
    backgcolor_pr->append_item (MenuKit::menuItem (
      kit, layout, STranslate::str (STranslate::ColLIGHTGRAY),
      new SetColourCallback (this, backg, 0.75, 0.75, 0.75)));  // grey75
    backgcolor_pr->append_item (MenuKit::menuItem (
      kit, layout, STranslate::str (STranslate::ColWHITE),
      new SetColourCallback (this, backg, 1.0, 1.0, 1.0)));  // white
  }

  // heads-up display colour submenu
  Menu* hudisplaycolor_pr = kit.pullright ();
  {
    colorRGB* hudisp = &scene_->col_hudisplay;
    hudisplaycolor_pr->append_item (MenuKit::menuItem (
      kit, layout, STranslate::str (STranslate::ColPURPLE),
      new SetColourCallback (this, hudisp, 0.5, 0, 1.0)));  // purple
    hudisplaycolor_pr->append_item (MenuKit::menuItem (
      kit, layout, STranslate::str (STranslate::ColORANGE),
      new SetColourCallback (this, hudisp, 1.0, 0.5, 0.0)));  // orange
    hudisplaycolor_pr->append_item (MenuKit::menuItem (
      kit, layout, STranslate::str (STranslate::ColCYAN),
        new SetColourCallback (this, hudisp, 0.0, 1.0, 1.0)));  // cyan
  }

  // anchor faces colour submenu
  Menu* anfacecolor_pr = kit.pullright ();
  {
    colorRGB* anface = &scene_->col_anchorface;
    anfacecolor_pr->append_item (MenuKit::menuItem (
      kit, layout, STranslate::str (STranslate::ColREDBROWN),
        new SetColourCallback (this, anface, 1.0, 0.25, 0.0, 1/*rebuild*/)));  // red-brown
    anfacecolor_pr->append_item (MenuKit::menuItem (
      kit, layout, STranslate::str (STranslate::ColGREEN),
        new SetColourCallback (this, anface, 0.0, 1.0, 0.0, 1)));  // green
  }

  // anchor edge colour submenu
  Menu* anedgecolor_pr = kit.pullright ();
  {
    colorRGB* anedge = &scene_->col_anchoredge;
    anedgecolor_pr->append_item (MenuKit::menuItem (
      kit, layout, STranslate::str (STranslate::ColYELLOW),
        new SetColourCallback (this, anedge, 1.0, 1.0, 0.25)));  // yellow
    anedgecolor_pr->append_item (MenuKit::menuItem (
      kit, layout, STranslate::str (STranslate::ColMAGENTA),
        new SetColourCallback (this, anedge, 1.0, 0.5, 1.0)));  // magenta
  }

  Menu* color_pr = kit.pullright ();  // colour pullright menu

  color_pr->append_item (MenuKit::menuItem (
    kit, layout, STranslate::str (STranslate::ColorOTHER),
    new ActionCallback(SceneMenus) (this, &SceneMenus::toggleColourDialog)));
  color_pr->append_item (kit.menu_item_separator ());
  color_pr->append_item (MenuKit::menuItem (
    kit, layout, STranslate::str (STranslate::ColorBACKGROUND), backgcolor_pr));
  color_pr->append_item (MenuKit::menuItem (
    kit, layout, STranslate::str (STranslate::ColorHEADSUP), hudisplaycolor_pr));
  color_pr->append_item (MenuKit::menuItem (
    kit, layout, STranslate::str (STranslate::ColorANCHORFACES), anfacecolor_pr));
  color_pr->append_item (MenuKit::menuItem (
    kit, layout, STranslate::str (STranslate::ColorANCHOREDGES), anedgecolor_pr));

  return color_pr;

} // colorPullright


Menu* SceneMenus::documentPulldown (WidgetKit& kit, const LayoutKit& layout)
{
  Menu* docinfo_pd = kit.pulldown ();

  docinfo_pd->append_item (MenuKit::menuItem (
    kit, layout, STranslate::str (STranslate::DocumentREFERENCES),
    new MenuGroupCallback (this, &SceneMenus::documentInfo, Scene3D::info_references)));
  docinfo_pd->append_item (MenuKit::menuItem (
    kit, layout, STranslate::str (STranslate::DocumentANNOTATIONS),
    new MenuGroupCallback (this, &SceneMenus::documentInfo, Scene3D::info_annotations)));
  docinfo_pd->append_item (MenuKit::menuItem (
    kit, layout, STranslate::str (STranslate::DocumentPARENTS),
    new MenuGroupCallback (this, &SceneMenus::documentInfo, Scene3D::info_parents)));
  docinfo_pd->append_item (MenuKit::menuItem (
    kit, layout, STranslate::str (STranslate::DocumentATTRIBUTES),
    new MenuGroupCallback (this, &SceneMenus::documentInfo, Scene3D::info_attributes)));
  docinfo_pd->append_item (MenuKit::menuItem (
    kit, layout, STranslate::str (STranslate::DocumentTEXTURES),
    new MenuGroupCallback (this, &SceneMenus::documentInfo, Scene3D::info_textures)));

  return docinfo_pd;

} // documentPulldown


// Menu* SceneMenus::optionsPulldown (WidgetKit& kit, const LayoutKit& layout)
// {
// enable motion and velocity control --> option submenu under navigate pulldown
// show framerate and twosided polygons --> display (view) pulldown
// }


Menu* SceneMenus::helpPulldown (WidgetKit& kit, const LayoutKit& layout)
{
  Menu* help_pd = kit.pulldown ();

  help_pd->append_item (MenuKit::menuItem (
    kit, layout, STranslate::str (STranslate::HelpOVERVIEW),
    new ActionCallback(Scene3D) (scene_, &Scene3D::doShowHelp), "F1"));
  help_pd->append_item (MenuKit::disable (MenuKit::menuItem (
    kit, layout, STranslate::str (STranslate::HelpINDEX), (Action*) nil)));
//  help_pd->append_item (kit.menu_item_separator ());
  help_pd->append_item (MenuKit::menuItem (
    kit, layout, STranslate::str (STranslate::HelpABOUTPRODUCT),
    new ActionCallback(SceneMenus) (this, &SceneMenus::toggleAbout)));

  return help_pd;

} // helpPulldown



// updateAbout moved to dialogs.C



/*** settings done with radio menus ***/


void SceneMenus::movementMode (int mode)
{ // menu callback, group updated by InterViews
  scene_->setNavigationMode (mode);
  scene_->giveNavigationHint ();
}


void SceneMenus::navigationMode (int mode)
{ // extern callable, update group

  TelltaleState* ts = 0;

  switch (mode)
  {
    case NavMode::flip_obj:
      ts = navflipobjstate_;
    break;
    case NavMode::walk_around:
      ts = navwalkstate_;
    break;
    case NavMode::fly_1:
      ts = navflystate_;
    break;
    case NavMode::fly_2:
      ts = navflytostate_;
    break;
    case NavMode::heads_up:
      ts = navheadsupstate_;
    break;
  }

  if (ts)  // update current item of radio menu group
  {
    ts->set (TelltaleState::is_chosen, true);

    movementMode (mode);
  }

} // navigationMode


void SceneMenus::selectionMode (int selmode)
{
  scene_->selectionMode (selmode);
}


void SceneMenus::twosidedPolys (int mode)
{
  scene_->twosidedpolys (mode);
  scene_->redraw ();
}


void SceneMenus::doLighting (int mode)
{
  scene_->dolighting (mode);
  scene_->redraw ();
}


void SceneMenus::highlightingMode (int mode)
{
  scene_->linksColor (mode);

  if (scene_->linksActive ())  // when anchors are highlighted
    scene_->redraw ();         // redraw whole window
}


void SceneMenus::setDisplayMode (int mode)
{ // menu callback, group updated by InterViews
  scene_->mode (mode);
  scene_->redraw ();  // redraw whole window
}


void SceneMenus::displayMode (int mode, int interactive)
{ // extern callable, update group

  TelltaleState* ts = 0;

  switch (mode)
  {
    case Scene3D::same_mode:
      ts = interactive ? intsame_ : 0;
    break;
    case ge3d_wireframe:
      ts = interactive ? intwire_ : wireframe_;
    break;
    case ge3d_hidden_line:
      ts = interactive ? inthline_ : hiddenline_;
    break;
    case ge3d_flat_shading:
      ts = interactive ? intflat_ : flatshading_;
    break;
    case ge3d_smooth_shading:
      ts = interactive ? intsmooth_ : smoothshading_;
    break;
    case ge3d_texturing:
      ts = interactive ? inttexture_ : texturing_;
    break;
  }

  if (ts)  // update current item of radio menu group
  {
    ts->set (TelltaleState::is_chosen, true);

    if (interactive)
      scene_->modeInteract (mode);
    else
      setDisplayMode (mode);  // and redraw
  }

} // DisplayMode


void SceneMenus::setInteractiveMode (int mode)
{
  scene_->modeInteract (mode);
  // no need to redraw when called from menu
}


/*** callback actions ***/


void SceneMenus::loadDemo (const char* demo)
{
  char filename [512];

  strcpy (filename, demopath_);
  strcat (filename, demo);
//cerr << "loading demo " << filename << endl;

  scene_->readSceneFile (filename);
  scene_->setTitle (filename);
  scene_->reset ();  // and redraw
}


void SceneMenus::browseDemo (const char* demo, const point3D& position, const point3D& lookat)
{
  char filename [512];

  strcpy (filename, demopath_);
  strcat (filename, demo);

  scene_->readSceneFile (filename);
  scene_->setTitle (filename);
  scene_->setCamera (position, lookat);
  scene_->reset ();  // and redraw
}


void SceneMenus::buildOpenFC ()
{
  DEBUGNL ("SceneMenus::buildOpenFC");

  Resource::unref (openfc_);  // destroy old dialog

  WidgetKit& kit = *kit_;
  kit.begin_style ("Open");
  Style* style = kit.style ();

  style->attribute ("name", STranslate::str (STranslate::DialogTitleOPENFILE));
  style->attribute ("open", STranslate::str (STranslate::DialogButtonOPEN));
  style->attribute ("cancel", WTranslate::str (WTranslate::CANCEL, slanguage));
  style->attribute ("subcaption", STranslate::str (STranslate::DialogStringFILENAME));

  openfc_ = new WFileChooser ("", &kit, style, 0, "*.wrl*");

  Resource::ref (openfc_);  // prevent immediate destruction

  kit.end_style ();  // "Open"

} // buildOpenFC


void SceneMenus::openFile ()
{
  if (!openfc_)
    buildOpenFC ();
  // otherwise reuse existent file chooser

  if (openfc_->post_for_aligned (scene_->appwin (), 0.5, 0.5))  // "OK"
  {
    const char* filename = openfc_->selected ();

    int res = scene_->readSceneFile (filename);
    scene_->setTitle (filename);
    scene_->reset ();  // and redraw

    if (res)  // error occured
    {
      RString errormsg (STranslate::str (STranslate::MessageERRORONREADING));  // ready to append filename
      errormsg += filename;

      MessageBox::message (
        slanguage, scene_->appwin (), errormsg,
        STranslate::str (STranslate::MsgboxtitleERROR), MessageBox::Ok
      );

    }  // invalid file
  }  // OK was pressed

} // openFile


void SceneMenus::saveVRMLScene ()
{
  // file format should be an option in save dialog (TODO)
  saveScene (Scene3D::write_VRML, "*.wrl");
}


void SceneMenus::saveSDFScene ()
{
  saveScene (Scene3D::write_SDF, "*.sdf");
}


void SceneMenus::saveOriginalScene ()
{
  saveScene (Scene3D::write_ORIGINAL, "");
}


void SceneMenus::saveScene (int format, const char* filemask)
{
  if (!scene_->supportsOutputformat (format))
  {
    MessageBox::message (
      slanguage, scene_->appwin (), STranslate::str (STranslate::MessageERRORNOINPUTFILE),
      STranslate::str (STranslate::MsgboxtitleERROR),
      MessageBox::Ok
    );
    return;
  }

  WidgetKit& kit = *kit_;
  kit.begin_style ("Save");
  Style* style = kit.style ();

  style->attribute ("name", STranslate::str (STranslate::DialogTitleSAVEFILE));
  style->attribute ("open", STranslate::str (STranslate::DialogButtonSAVE));
  style->attribute ("cancel", WTranslate::str (WTranslate::CANCEL, slanguage));
  style->attribute ("subcaption", STranslate::str (STranslate::DialogStringFILENAME));

  // should set path here

  WFileChooser* fc = new WFileChooser ("", &kit, style, 0, filemask);
  Resource::ref (fc);  // prevent immediate destruction

  if (fc->post_for_aligned (scene_->appwin (), 0.5, 0.5))  // "OK"
  {
    const char* filename = fc->selected ();
    RString messagestring (filename);
    messagestring += RString ("\n") + STranslate::str (STranslate::DialogStringOVERWRITEEXISTENTFILE);

    // can write if file not yet exists or user wants to overwrite it
    int write = access (filename, F_OK) ||
    MessageBox::message (
      slanguage, scene_->appwin (), messagestring,
      STranslate::str (STranslate::DialogTitleSAVEFILE), MessageBox::Ok | MessageBox::Cancel
    ) == MessageBox::Ok;

    if (write)
    {
      ofstream ofs (filename);
      int ok;

      if (gecontext_)
        gecontext_->setCursor (HgCursors::instance ()->hourglass ());

      ok = ofs && !scene_->writeScene (ofs, format);

      if (gecontext_)
        gecontext_->resetCursor ();

      if (!ok)
      {
        messagestring = STranslate::str (
          /* ok ? STranslate::MessageSUCCESSFULLYWRITTEN : */ STranslate::MessageERRORONWRITING
        );
        messagestring += filename;  // append file name

        MessageBox::message (
          slanguage, scene_->appwin (), messagestring,
          STranslate::str (/*ok ? STranslate::DialogTitleSAVEFILE : */ STranslate::MsgboxtitleERROR),
          MessageBox::Ok
        );
      }
    } // write file

  }  // OK was pressed

  Resource::unref (fc);  // destroy file chooser

  kit.end_style ();  // "Save"
} // saveScene


void SceneMenus::saveDepth ()  // will be merged into saveScene
{
// must query contents of Z-buffer for reasonable performance!
#ifdef TODO  /* saveDepth */
  ofstream ofs ("tmpDepthFile.pgm");
  scene_->writeDepth (ofs, 320, 200, Scene3D::depth_PGMbin);
#endif
} // saveScene


void SceneMenus::documentInfo (int info)
{
  scene_->docinfo (info);  // Scene3D::info_...
}


// dialog creating functions moved to dialogs.C:
// buildErrorDialog
// buildSpaceballDialog
// buildNavigationSettingsDialog
// buildColourDialog
// toggleColourDialog


void SceneMenus::applyColours (ColourDialog* coldlg)
{
  DEBUGNL ("SceneMenus::applyColours");

  // be sure to use same order as in buildColourDialog above

  float r, g, b;
  colorRGB* col;

  if (coldlg->modified (0))
  { col = &scene_->col_background;
    coldlg->getRGB (r, g, b, 0);
    initRGB (*col, r, g, b);
  }
  if (coldlg->modified (1))
  { col = &scene_->col_hudisplay;
    coldlg->getRGB (r, g, b, 1);
    initRGB (*col, r, g, b);
  }
  if (coldlg->modified (2))
  { col = &scene_->col_anchorface;
    coldlg->getRGB (r, g, b, 2);
    initRGB (*col, r, g, b);
    scene_->colorRebuild ();
  }
  if (coldlg->modified (3))
  { col = &scene_->col_anchoredge;
    coldlg->getRGB (r, g, b, 3);
    initRGB (*col, r, g, b);
  }
  if (coldlg->modified (4))
  { col = &scene_->col_viewinglight;
    coldlg->getRGB (r, g, b, 4);
    initRGB (*col, r, g, b);
  }

  colourChanged ();  // redraw scene

} // applyColours


void SceneMenus::colourChanged ()
{
  // set new background (may have changed)
  ge3dBackgroundColor (&scene_->col_background);
  scene_->redraw ();  // redraw with new colour
}


void SceneMenus::anchorMotionChanged ()
{
  scene_->anchorMotion ((anchormotionstate_->flags () & TelltaleState::is_chosen) ? 1 : 0);

  if (scene_->linksActive ())  // when anchors are highlighted
    scene_->redraw ();     // redraw whole window
}


void SceneMenus::velocityControlChanged ()
{
  scene_->velocityControl ((velocitycontrolstate_->flags () & TelltaleState::is_chosen) ? 1 : 0);
}


void SceneMenus::arbitraryRotationChanged ()
{
  scene_->arbitraryRotations ((arbitraryrotstate_->flags () & TelltaleState::is_chosen) ? 1 : 0);
}


void SceneMenus::setCollisionDetection (int flag)
{
  scene_->collisionDetection (flag);
  colldetectionstate_->set (TelltaleState::is_chosen, flag);
}


void SceneMenus::collisionDetectionChanged ()
{
  scene_->collisionDetection ((colldetectionstate_->flags () & TelltaleState::is_chosen) ? 1 : 0);
}


void SceneMenus::toggleShowAnchors ()
{
  int flag = (showanchorstate_->flags () & TelltaleState::is_chosen) ? 1 : 0;
  showanchorstate_->set (TelltaleState::is_chosen, !flag);
  showAnchorsChanged ();
}


void SceneMenus::showAnchorsChanged ()
{
  scene_->activateLinks ((showanchorstate_->flags () & TelltaleState::is_chosen) ? 1 : 0);
  scene_->redraw ();
  scene_->statusMessage (STranslate::str (STranslate::AnchorHintACTIVATION));
}


void SceneMenus::aaliasingChanged ()
{
  int aa = (aaliasingstate_->flags () & TelltaleState::is_chosen) ? (int) ge3d_aa_all : 0;
  ge3dAntialiasing (aa);
  scene_->redraw ();
}


void SceneMenus::toggleAntialiasing ()
{
  aaliasingstate_->set (TelltaleState::is_chosen, !aaliasingstate_->test (TelltaleState::is_chosen));
  aaliasingChanged ();
}


void SceneMenus::viewingLightChanged ()
{
  scene_->viewingLight ((viewlgtstate_->flags () & TelltaleState::is_chosen) ? 1 : 0);
  scene_->redraw ();
}

void SceneMenus::toggleTextureLighting ()
{
  scene_->textureLighting (!scene_->textureLighting ());
  scene_->redraw ();
}

void SceneMenus::resetView ()
{
  scene_->restoreCamera ();
  inputhand_->reset ();
  scene_->redraw ();
}


void SceneMenus::levelView ()
{
  Camera* cam = scene_->getCamera ();
  if (cam)
  { cam->makeHoricontal ();
    inputhand_->reset ();
    scene_->redraw ();
  }
  // center of rotation is camera position
  // for flip object center of scene should be used, but
  // makehoricontal is not really necessary for flip object
}


void SceneMenus::untiltView ()
{
  Camera* cam = scene_->getCamera ();
  if (cam)
  { cam->untilt ();
    inputhand_->reset ();
    scene_->redraw ();
  }
}


void SceneMenus::addViewpoint (
  const char* name, QvPerspectiveCamera* pcam, QvOrthographicCamera* ocam
)
{
  // cerr << "adding camera '" << name << "' to viewpoint menu." << endl;
  if (!viewpointsPr_)
    return;

  int num = (int) viewpointsPr_->item_count () + 1;  // let numbers start with 1
  char defname [32];
  char number [16];
  strcpy (defname, STranslate::str (STranslate::NavigateVIEWPOINT));
  sprintf (number, " %d", num);
  strcat (defname, number);  // "Camera NN"
  if (!name || !*name)
    name = defname;

  viewpointsPr_->append_item (MenuKit::menuItem (
    *kit_, *layout_, name,
    new ViewpointCallback (this, name, pcam, ocam), (num < 10) ? number+1 : (const char*) 0));
}


void SceneMenus::clearViewpoints ()
{
  if (!viewpointsPr_)
    return;

  int i = (int) viewpointsPr_->item_count ();

  while (i--)
    viewpointsPr_->remove_item (i);
}


void SceneMenus::selectViewpoint (int i)
{
  if (!viewpointsPr_)
    return;

  int num = (int) viewpointsPr_->item_count ();
  if (i >= 0 && i < num)
  {
    MenuItem* item = viewpointsPr_->item (i);
    item->action ()->execute ();
  }
}


void SceneMenus::removeSelection ()
{
  if (scene_->selectObj (0) || scene_->selectNode (0))
    scene_->redraw ();
}


void SceneMenus::printData ()
{
  scene_->printInfo (0);
}


void SceneMenus::printAll ()
{
  scene_->printInfo (1);
}


void SceneMenus::cameraInfo ()
{
  Camera* cam = scene_->getCamera ();

  if (!cam)
  { cerr << "no camera!";
    return;
  }

  point3D p;

  cerr << "current camera:" << endl;
  cam->print ();

  cam->getposition (p);
  cerr << "camera position: " << p << endl;
  cam->getlookat (p);
  cerr << "lookat position: " << p << endl;
  cam->getupvector (p);
  cerr << "up direction   : " << p << endl
       << endl;
}


// dialog opening/closing functions moved to dialogs.C:
// toggleAbout
// toggleErrorDialog
// closeErrorDialog
// toggleSpaceballDialog
// closeSpaceballDialog


int SceneMenus::getSballFlag (int i)
{
#ifdef SPACEBALL
  TelltaleState* t = sbflagstate_ [i];
  return (int) t->test (TelltaleState::is_chosen);
#else
  return i;
#endif
}

int SceneMenus::toggleSballFlag (int i)
{
#ifdef SPACEBALL
  TelltaleState* t = sbflagstate_ [i];

  int newflag = !t->test (TelltaleState::is_chosen);
  t->set (TelltaleState::is_chosen, newflag);

  sb_checkbuttons (i);

  return newflag;
#else
  return i;
#endif
}


void SceneMenus::toggleSballMode ()
{
#ifdef SPACEBALL
  // spaceball metaphors: flip (manipulate object) and walk (manipulate view)
  if (scene_->navigationMode () == NavMode::flip_obj)
    navigationMode (NavMode::walk_around);
  else
    navigationMode (NavMode::flip_obj);
#endif
}


void SceneMenus::sb_checkbuttons (int i)
{
#ifdef SPACEBALL
  // ensure at least one of translation/rotation is turned on
  // button i is the most recently toggled one
  TelltaleState* t = sbflagstate_ [i];
  if (t->test (TelltaleState::is_chosen))
    return;

  if (i == sb_translate)
    sbflagstate_ [sb_rotate]->set (TelltaleState::is_chosen, true);
  else if (i == sb_rotate)
    sbflagstate_ [sb_translate]->set (TelltaleState::is_chosen, true);
#endif
}


float SceneMenus::sballSensitivity () const
{
  // internally stored as power of 2
  if (sbsensitivity2_ < 0)
    return 1.0 / (1 << -sbsensitivity2_);
  return (float) (1 << sbsensitivity2_);
}


void SceneMenus::updateSensitivity ()
{
#ifdef SPACEBALL
  if (!sbsensfieldb_)
    return;

  char buf [64];
  if (sbsensitivity2_ < 0)
    sprintf (buf, "1/%d", 1 << -sbsensitivity2_);
  else
    sprintf (buf, "%d", 1 << sbsensitivity2_);

  sbsensfieldb_->field (buf);
#endif
}


void SceneMenus::decreaseSballSensitivity ()
{
  if (sbsensitivity2_ > -10)
  { sbsensitivity2_--;
    updateSensitivity ();
  }
}


void SceneMenus::increaseSballSensitivity ()
{
  if (sbsensitivity2_ < 10)
  { sbsensitivity2_++;
    updateSensitivity ();
  }
}


void SceneMenus::resetSballSensitivity ()
{
  if (sbsensitivity2_ != 0)
  { sbsensitivity2_ = 0;
    updateSensitivity ();
  }
}
