/*
 * $Id: imview.cxx,v 4.22 2004/06/24 05:08:45 hut66au Exp $
 *
 * Imview, the portable image analysis application
 * http://www.cmis.csiro.au/Hugues.Talbot/imview
 * ----------------------------------------------------------
 *
 *  Imview is an attempt to provide an image display application
 *  suitable for professional image analysis. It was started in
 *  1997 and is mostly the result of the efforts of Hugues Talbot,
 *  Image Analysis Project, CSIRO Mathematical and Information
 *  Sciences, with help from others (see the CREDITS files for
 *  more information)
 *
 *  Imview is Copyrighted (C) 1997-2001 by Hugues Talbot and was
 *  supported in parts by the Australian Commonwealth Science and 
 *  Industry Research Organisation. Please see the COPYRIGHT file 
 *  for full details. Imview also includes the contributions of 
 *  many others. Please see the CREDITS file for full details.
 *
 *  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., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
 * */

/*------------------------------------------------------------------------
 *
 *  The Main part of imView, an image viewer program for X11
 *  by Hugues Talbot	26 Oct 1997
 *
 * This program is available free of charge for anyone to use under no
 * guarantee of any kind, expressed or implied.
 *
 *-----------------------------------------------------------------------*/

#include <imcfg.h> // very important to have at the top there
#include "imnmspc.hxx"


#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Menu_Bar.H>
#include <FL/Fl_Box.H>
#include <FL/Fl_Image.H>
#include <FL/Fl_File_Chooser.H>
#include <FL/Fl_Help_Dialog.H>
#include <FL/Fl_File_Icon.H>
#include <FL/x.H>
#include "imunistd.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#ifdef WIN32_NOTCYGWIN
#   include <windows.h>
#   include <tchar.h>
#   include <io.h>
#else
#   include <dirent.h>  // should always be there because FLTK defines it.
#endif

#ifdef HAVE_PTHREADS
#include <pthread.h> // oh my God.
#endif

#include <map>    // C++ stl maps
#include <string> // the C++ ones

#include "imview.hxx"
#include "imviewWindow.hxx"
#include "io/imSystem.hxx"
#include "menubar.hxx"
#include "imageIO.hxx"
#include "pointfile.hxx"
#include "imageViewer.hxx"
#include "printPrefs.hxx"
#include "savePrefs.hxx"
#include "imageInfo.hxx"
#include "transferFunction.hxx"
#include "spectraBox.hxx"
#include "profileBox.hxx"
#include "saveSpect.hxx"
#include "printSpect.hxx"
#include "userPrefs.hxx"
#include "progressInfo.hxx"
#include "nocase.hxx"
#include "io/newprefio.hxx"
#include "tmpMsg.hxx"
#include "bivHist.hxx"
#include "machine.hxx"
#include "server/imserver.hxx"
#include "server/interpreter.hxx"
#include "rawImage.hxx"
#include "annotatePoints.hxx"
#include "toolbar.hxx"
#include "io/readMagick.hxx" // for the initImageMagick function
#include "transferRGBFunction.hxx"
#include "sliceSlider.hxx" // generic panel with linked slider/value
#include "patchlevel.h"

typedef map<string,image_psfile>::const_iterator cmi;  // const Map iterator

/*-- miscellaneous global variables  ---*/
char                  appName[DFLTSTRLEN];
char                **extpathlist = 0; // list of all the LUT paths

// components of the application
imviewWindow          *mainWindow = 0;
imageViewer           *mainViewer = 0;
imViewMenuBar         *mainMenuBar = 0;
imageIO               *IOBlackBox = 0;
Fl_Box                *titleBox = 0, *imageBox = 0;
Fl_Image              *titleImage = 0;
pointfile             *PtFileMngr = 0; // point file manager
Fl_File_Chooser       *ImviewFC = 0; // File chooser dialog

// objects created using fluid
printprefs            *printPrefsPanel = 0; // print preferences panel
saveprefs             *savePrefsPanel = 0;  // save preferences panel
imageinfo             *imageInfoPanel = 0;  // image information panel
transfer              *transferEditorPanel = 0; // transfer function editor panel
transferRGB           *transferRGBEditorPanel = 0; // same as above for RGB images
spectra               *spectraPanel = 0;    // spectrum display for hyperspectral images
spectra               *depthProfile = 0;    // profile down the depth axis for 3D image
profile               *profilePanel = 0;    // 1d profiles 
savespect             *saveSpectPanel = 0;  // saves a spectrum
printspect            *printSpectPanel = 0; // prints a spectrum
userprefs             *userprefsPanel = 0;  // deals with the user preferences
tmpmsg                *tmpmsgPanel = 0;     // for temporary messages
bivhist               *bivHistPanel = 0;    // bivariate histogram
progress              *progressPanel = 0;   // progress slider panel
rawimage              *rawImagePanel = 0;   // unrecognized format, manually entered parms
Fl_Help_Dialog        *help = 0;            // the Help dialog...
annotatept            *annotatePointPanel = 0; // point annotation
toolbar               *toolbarPanel = 0;    // as the name implies, a primitive toolbar
slideinput            *slice3D = 0, *sliceComponent = 0, *sliceSeries = 0;

// other objects
imprefs               *fileprefs = 0;
char                   currentprefspath[DFLTSTRLEN];
char                   installpath[DFLTSTRLEN];
interpreter           *imview_interpreter = 0; // used to access the server remotely 
linkmanager           *imview_linkmanager = 0; // essentially the client to other imview servers.

// threads & server
#ifdef HAVE_PTHREADS
pthread_t              serverthread;
bool                   serverIsRunning = false;
#endif // HAVE_PTHREADS

Semaphore              access_debug, access_error; // the output channels must be protected
bool                   server_terminate = false;
bool                   main_terminate = false;
imview_server         *is = 0;
double                 simple_progress = 0.0; 
volatile int           serverPortNumber = 0;
int                    portfileDescriptor = -1;
int                    pipepair[2];
char                   portfilename[DFLTSTRLEN];

// these depend on the size of the physical screen
int                    appMaxWidth, appMaxHeight;

// useful strings
string                 truestr          = "True";
string                 falsestr         = "False";
string                 BWstr            = "BW";
string                 GREYstr          = "Grey";
string                 COLOURstr        = "Colour";
string                 CONSTRAINEDstr   = "Constrained";
string                 UNCONSTRAINEDstr = "Unconstrained";

// splash screen image
const char splashImg[] = "imview_banner.jpg";

// mapping real PS file names to temporary files
map <string,image_psfile>   psfilemap;

// mapping pathnames to image header, useful for raw images
map <string, RAWFILE_HEADER> rawfilemap;

// various flag/preferences
int                    debugIsOn = 0;
int                    stopDebug = 0;
int                    serverDebug = 0; // explicitely to debug the server
bool                   lutWraparound = false; // LUT behaviour for regions > 255
volatile bool          syncDisplay = false;
volatile bool          hasSynced = false;
bool                   hideMainMenu = false; // hides but still enables the main menu
bool                   disableMainMenu = false; // disable but stil shows the main menu
bool                   disableIOKeys = false; // disables *some* keyboard shortcuts
bool                   disableQuit = false; // Imview just won't quit now...
int                    makeImageCharIfFits = 1; // if a non-CHAR image fits in a char image, should we call it a char image and save memory?
int                    RGBdisplaysRGB = 1; // should 3-spectra images and RGB images be displayed as RGB or multi-spectra images?
int                    fitCharOverAllSamples = 1; // Should a multi-sample non-char image be fitcharred over all samples or only the one displayed?
int                    fitCharOverAllSlices = 1; // Should we fit a 3D image over all slices?
bool                   performFork = false; // useful when called from system()
bool                   ntChild = false; // specific to windows.
bool                   darwinChild = false; // specific to Darwin/MacOS/Carbon
int                    zimageArgs = 0;
int                    appendPoints = 0;
int                    applyTransferToAll = 0;
int                    deleteAfter = 0; // should we delete the images after we close them?
int                    startServer = 0;
bool                   useOffscreenBuff = true;  // if true, use an off-screen buffer to draw image into at zoom = 1.0. 
bool                   useMagick = true; // if false, the use of the ImageMagick library is disallowed (ImageMagick crashes easily)
bool                   forceMagick = false; // if true, forces the use of ImageMagick to the exclusion of natively supported image format
bool                   simplePointfile = false; // if true, export simpler versions of the pointfiles (no annotation, no colour support)
int                    menuheight = MENUHEIGHT;
displaymode            displaymodepref = IMV_DISPLAY_WINDOW_FIT_IMG;
bool                   fullScreen = false;

// a couple of temporary file name buffer

char                  *imTempFileName = 0;
//const char  *psonlyfile;

double                 persistentGamma = 1.0;

// the nasty signals longjump buffer
static im_jmp_buf      abortenv;

// an RGB description of the FL colours

// hand-coded Look-up table for the overlay plane.
unsigned char FL_COLOURS[MAX_FL_COLOURS][3] = {
{0,     0,    0},    // 0 black
{255,   0,    0},    // 1 Red
{0,   255,    0},    // 2 Green
{255, 255,    0},    // 3 Yellow
{0,     0,  255},    // 4 Blue
{255,   0,  255},    // 5 Magenta
{0,   255,  255},    // 6 cyan
{255, 255,  255},    // 7 white
// other colours
{      0,    127,    255}, {    127,    255,      0}, {    255,      0,    127}, {    148,    148,    148}, {      0,    255,    128},
{    128,      0,    255}, {    255,    128,      0}, {      1,      0,    159}, {      0,    159,      1}, {    159,      1,      0},
{     96,    254,    255}, {    255,     96,    254}, {    254,    255,     96}, {     21,    125,    126}, {    126,     21,    125},
{    125,    126,     21}, {    116,    116,    255}, {    116,    255,    116}, {    255,    116,    116}, {      0,    227,    228},
{    228,      0,    227}, {    227,    228,      0}, {     28,     27,    255}, {     27,    255,     28}, {    255,     28,     27},
{     59,     59,     59}, {    176,    195,    234}, {    255,    196,    175}, {     68,    194,    171}, {    171,     68,    194},
{    194,    171,     68}, {     71,    184,     72}, {     72,     71,    184}, {    184,     72,     71}, {    188,    255,    169},
{     63,    179,    252}, {    179,    252,     63}, {    252,     63,    179}, {      0,      9,     80}, {      9,     80,      0},
{     80,      0,      9}, {    250,    175,    255}, {    213,    134,    199}, {     95,    100,    115}, {      0,    163,    188},
{    163,    188,      0}, {    188,      0,    163}, {      0,     73,    203}, {     73,    203,      0}, {    203,      0,     73},

{      0,    189,     94}, {     94,      0,    189}, {    189,     94,      0}, {    119,    243,    187}, {     32,    125,     55},
{     55,     32,    125}, {    125,     55,     32}, {    185,    102,    255}, {    255,    185,    102}, {    168,    209,    120},
{    119,    166,    208}, {    192,     96,    135}, {     41,    255,    182}, {    130,    153,     83}, {     55,     88,    247},
{     55,    247,     89}, {    247,     55,     88}, {      0,     75,     87}, {     75,     87,      0}, {     87,      0,     75},
{     59,    135,    200}, {    127,    213,     51}, {    162,    255,    255}, {    182,     37,    255}, {    255,    182,     37},
{    117,     57,    228}, {    210,    163,    142}, {    228,    117,     57}, {    246,    255,    193}, {    123,    107,    188},
{    107,    194,    123}, {      5,     59,    145}, {     59,    145,      5}, {    145,      5,     59}, {    198,     39,    119},
{     23,    197,     40}, {     40,     23,    197}, {    197,     40,     23}, {    158,    199,    178}, {    121,    201,    255},
{    223,    223,    134}, {     84,    253,     39}, {     15,    203,    149}, {    149,     15,    203}, {    203,    149,     15},
{     90,    144,    152}, {    139,     75,    143}, {    132,     97,     71}, {    219,     65,    224}, {    224,    219,     65},

{     40,    255,    255}, {     69,    223,    218}, {      0,    241,     74}, {     74,      0,    241}, {    241,     74,      0},
{     51,    171,    122}, {    227,    211,    220}, {     87,    127,     61}, {    176,    124,     90}, {     13,     39,     36},
{    255,    142,    165}, {    255,     38,    255}, {    255,    255,     38}, {    107,     50,     83}, {    165,    142,    224},
{      9,    181,    255}, {    181,    255,      9}, {    255,      9,    181}, {     70,    238,    140}, {      5,     74,    255},
{    255,      5,     74}, {     51,     84,    138}, {    101,    172,     31}, {     17,    115,    177}, {      0,      0,    221},
{      0,    221,      0}, {    221,      0,      0}, {    200,    255,    220}, {     50,     41,      0}, {    205,    150,    255},
{    116,     45,    178}, {    189,    255,    113}, {     44,      0,     47}, {    171,    119,     40}, {    255,    107,    205},
{    172,    115,    177}, {    236,     73,    133}, {    168,      0,    109}, {    207,     46,    168}, {    203,    181,    188},
{     35,    188,    212}, {     52,     97,     90}, {    184,    209,     39}, {    152,    164,     41}, {     70,     46,    227},
{    227,     70,     46}, {    255,    156,    211}, {    222,    146,     98}, {     95,     56,    136}, {    152,     54,    102},

{      0,    142,     86}, {     86,      0,    142}, {    142,     86,      0}, {     96,    223,     86}, {     46,    135,    246},
{    120,    208,      4}, {    158,    233,    212}, {    214,     92,    177}, {     88,    147,    104}, {    147,    240,    149},
{    148,     93,    227}, {    133,    255,     72}, {    194,     27,    209}, {    255,    255,    147}, {      0,     93,     44},
{    158,     36,    160}, {      0,    233,    182}, {    217,     94,     96}, {     88,    103,    218}, {     38,    154,    163},
{    139,    114,    118}, {     43,      0,     94}, {    174,    164,    113}, {    114,    188,    168}, {    119,     23,      0},
{     93,     86,     42}, {    202,    226,    255}, {    155,    191,     80}, {    136,    158,    255}, {     62,    247,      0},
{     88,    146,    234}, {    229,    183,      0}, {     36,    212,    110}, {    161,    143,      0}, {    210,    191,    105},
{      0,    164,    133}, {     89,     30,     41}, {    132,      0,    164}, {     42,     89,     30}, {    217,    222,    178},
{     11,     22,    121}, {     22,    107,    221}, {    255,    151,     69}, {      3,    158,     45}, {     45,      3,    158},
{    158,     45,      3}, {     29,     42,     86}, {     22,    122,      9}, {    110,    209,    213}, {     57,    221,     53},

{     91,    101,    159}, {     45,    140,     93}, {     37,    213,    247}, {      0,     34,    185}, {     34,    185,      0},
{    185,      0,     34}, {    172,      0,    236}, {     78,    180,    210}, {    221,    107,    231}, {     43,     49,    162},
{     49,    162,     43}, {    162,     43,     49}, {    213,    248,     36}, {    214,      0,    114}, {    248,     36,    213},
{    243,     34,    149}, {    167,    158,    185}, {    224,    122,    144}, {    149,    245,     34}, {     98,     31,    255},
{    255,     98,     31}, {    193,    200,    152}, {     95,     80,    255}, {     63,    123,    128}, {     72,     62,    102},
{    148,     62,    255}, {    108,    226,    151}, {    255,     99,    159}, {    126,    255,    226}, {    136,    223,     98},
{    255,     95,     80}, {     15,    153,    225}, {    211,     41,     73}, {     41,     71,    212}, {    187,    217,     83},
{     79,    235,    180}, {    127,    166,      0}, {    243,    135,    251}, {      0,     41,    229}, {    229,      0,     41},
{    216,    255,     82}, {    249,    174,    141}, {    255,    215,    249}, {     79,     31,    167}, {    167,     79,     31},
{    185,    102,    213}, {     83,    215,    255}, {     40,      2,      4}, {    220,    171,    224}, {      4,      0,     41},

{     90,     50,      6}, {    113,     15,    221}, {    221,    113,     15}, {    115,      0,     33}
};

// page sizes in points
float page_formats[PAGESIZE_NB][2] = {
    {-1, -1},       /* automatic */
    {421.0, 595.0}, /* A5 */
    {501.0, 709.0}, /* B5 */
    {595.0, 842.0}, /* A4 */
    {612.0, 792.0}, /* Letter */
    {612.0, 1008.0}, /* Legal (not sure */
    {842.0, 1190.0} /* A3 */
};

/* thanks to Python */ 
/* imview... */
const char *silly_messages[] = {
    "is dead, Jim", 
    "has joined the Choir Invisible",
    "has run down the curtain",
    "has gone to meet its Developer (who says Hi, thx for using imview!)",
    "is pushing up the daisies",
    "has passed on",
    "is no more",
    "has ceased to be",
    "has expired",
    "is a stiff",
    "is bereft of artificial life",
    "may rest in peace",
    "'s computing processes are now history",
    "is off the twig",
    "has shuffled off this mortal coil",
    "is an ex-imview", 
    "has kicked the bucket",
    "a passé l'arme à gauche",
    "n'est plus",
    "nous a quitté",
    "has done itself in",
    NULL
};

// returns the Fl_Color closest to the RGB value given
Fl_Color fl_closest_colour(uchar r, uchar g, uchar b)
{
    return (fl_color_cube(r * (FL_NUM_RED - 1) / 255,
                          g * (FL_NUM_GREEN - 1) / 255,
                          b * (FL_NUM_BLUE - 1) / 255));
}

// returns the FLTK colour closest to the index in the FL_COLOUR array
Fl_Color fl_closest_index(int i)
{
    return (fl_closest_colour(FL_COLOURS[i][0],
                              FL_COLOURS[i][1],
                              FL_COLOURS[i][2]));
}

static void experimental_sync(void);

// VERY useful function.

// equivalent to the '<' operator.
// returns 0 if both strings are the same
// return 1 if a < b
// return -1 if a > b
int cmp_nocase(const string &a, const string &b)
{
    string::const_iterator p = a.begin();
    string::const_iterator q = b.begin();

    while (p != a.end() && q != b.end()) {
	if (toupper(*p) != toupper(*q))
	    return ((toupper(*p) < toupper(*q)) ? -1: 1);
	++p;
	++q;
    }
    return ((b.size() == a.size()) ? 0 : (a.size() < b.size()) ? -1:1); 
}

// case-insensitive compare strings 
// the nocase operator, equivalent to the '<' operator, but for
// case-independent comparisons.
bool Nocase::operator () (const string &x, const string &y) const
{
    string::const_iterator p = x.begin();
    string::const_iterator q = y.begin();

    while (p!=x.end() && q!=y.end() && toupper(*p) == toupper(*q)) {
	++p;
	++q;
    }
    if (p == x.end()) return q != y.end();
    return toupper(*p) < toupper(*q);
}

// the title banner

Fl_Box *bannerBox(int x, int y, int w, int h)
{
    static char message[200];

    
    sprintf(message,"Imview %s", patchlevel);
    dbgprintf("%s\n",message);
    Fl_Box *box = new Fl_Box(x,y,w,h,message);
    box->labelfont(FL_TIMES | FL_ITALIC | FL_BOLD);
    box->labelsize(24);
    box->labeltype(FL_SHADOW_LABEL);
    box->labelcolor(FL_WHITE);

    return box;
}



void showSimpleBanner(void)
{
    int tboxwidth, tboxheight, ret;
    static int splashwidth, splashheight, splashspp,i,j;
    static long splashbytes = 0;
    static unsigned char *splashScreen = 0;

    mainWindow->size_range(MINWIDTH,MINHEIGHT,SWIDTH,SHEIGHT-(MENUHEIGHT-menuheight));
    dbgprintf("Setting size limit in showSimpleBanner\n");
    mainWindow->size(SWIDTH,SHEIGHT-(MENUHEIGHT-menuheight));

    

    // show the splash screen if found
    // installpath is defined dynamically and splashImg is a constant char[] defined above.
    if (splashScreen == 0) {
	static char pathToSplash[DFLTSTRLEN];
        char tmppath[DFLTSTRLEN];
        
        for ( i = 0, j = 0; ; ) { // ever
            if ((installpath[i] != PATHSEP) && (installpath[i] != '\0')) {
                tmppath[j++] = installpath[i++];
            } else {
                tmppath[j] = '\0';
                dbgprintf("looking in %s\n", tmppath);
                snprintf(pathToSplash, DFLTSTRLEN, "%s%c%s", tmppath, SLASH, splashImg);

                // try to read it off a file
                if ((ret = IOBlackBox->read(pathToSplash, 0)) == 0) {
                    // fine, we could read the splash image
                    splashwidth = IOBlackBox->imageWidth();
                    splashheight = IOBlackBox->imageHeight();
                    splashspp = IOBlackBox->imageDepth();
                    splashbytes = splashwidth * splashheight * splashspp * sizeof(uchar);
                    splashScreen = (uchar *) malloc(splashbytes);
                    // take a permanent copy
                    memcpy(splashScreen, IOBlackBox->imageData(), splashbytes);
                    // set minimal information 
                    IOBlackBox->setImName(pathToSplash);
                    IOBlackBox->setCurrImgNbFrames(1);
                    break;
                } else {
                    warnprintf("showSimpleBanner: could not read file %s\n", pathToSplash);
                    // no joy there, try next string if possible
                    if (installpath[i] != '\0') {
                        j = 0;
                        i++;
                    } else
                        break;
                }
            }
        }     
    }
    // test again

    if (splashScreen != 0) {
	// then display it
	titleImage = new Fl_RGB_Image(splashScreen, splashwidth, splashheight);
	imageBox = new Fl_Box((mainWindow->w()-splashwidth)/2,
			  (mainWindow->h()-splashheight+menuheight)/2,
			  splashwidth, splashheight);
	titleImage->label(imageBox);
	mainWindow->add(imageBox);
	titleBox = bannerBox(5, 50, 160, 30);
    } else {

	tboxheight = TBOXHEIGHT;
	tboxwidth = mainWindow->w()-50; // say
    
	// show a simple text banner
	titleBox = bannerBox((mainWindow->w()-tboxwidth)/2,
			     (mainWindow->h()-tboxheight)/2,
			     tboxwidth, tboxheight);
    }
	    

    mainWindow->add(titleBox);
}

void removeSimpleBanner(void)
{
    if (titleBox) {      // && titleBox->visible()) {
	mainWindow->remove(titleBox);
	delete(titleBox);
	titleBox = 0;
	mainWindow->size_range(MINWIDTH,MINHEIGHT,appMaxWidth,appMaxHeight);
	dbgprintf("Removing size limits in removeSimpleBanner\n");
    }

    if (imageBox) {
	mainWindow->remove(imageBox);
	delete(imageBox);
	imageBox = 0;
    }
    if (titleImage) {
	delete(titleImage);
	titleImage = 0;
    }
}

static void printUnixUsage(char *fname)
{
    const char *newfname;
    char blankpad[DFLTSTRLEN];
    int  i = 0;

    newfname = myBaseName(fname);

    strcpy(blankpad, newfname);
    // converts to blanks
    while (blankpad[i] != '\0')
	blankpad[i++] = ' ';
    
    fprintf(stderr,
	    "Purpose: this application displays images\n" 
	    "Usage  : %s [-h] [-v] [-debug] [-stopdebug] [-h] [-v] [-C defaultLUT]\n" 
	    "         %s [-p pointfile] [-a] [-] [-mag defaultMagnification]\n" 
	    "         %s [-wt <sometitle>] [-delete] [-no_dblbuf]\n" 
	    "         %s [-hide_menubar] [-disable_menubar] [-disable_io_keys] \n"
	    "         %s [-disable_quit] [-locked] [-no_magick]\n"
	    "         %s [image1 [-c clut1]] [image2 [-c clut2]] ...\n"  
	    "\n" 
	    "%s-specific options:\n" 
	    "------------------------\n" 
	    "               - : reads an image from stdin\n" 
	    "              -a : reads and appends to an existing pointfile\n" 
	    "    -c <lutname> : Z-IMAGE type look-up table attached to preceding image\n"
	    " -C <defaultlut> : changes the default LUT for all images\n" 
	    "          -debug : prints gobs of debugging messages\n" 
	    "         -delete : deletes images after use\n" 
            "-disable_menubar : disable the main menu (but does not hide it)\n"
            "-disable_io_keys : disable I/O key shortcuts (open/close images, quit, etc)\n"
            "   -disable_quit : disable standard ways to quit the application (Esc, etc)\n"
            "            -fit : fits the image display to the window: no scrollbars\n"
            "   -force_magick : forces use of the ImageMagick library instead of native support\n"
	    "           -fork : sends itself to the background\n" 
	    "   -gamma <value>: specifies <value> as the default gamma\n"  
	    "              -h : this help\n" 
            "   -hide_menubar : hide the main menu (but does not disable it)\n"
            "         -locked : equivalent to all the disable_* and -hide_menubar flags set together\n" 
	    "    -mag <value> : changes the default magnification to <value>\n" 
            "      -no_dblbuf : do not use double-buffering at certain zoom factors\n"
	    "      -no_magick : use of library ImageMagick is disallowed (as it can be quite buggy)\n"
	    "  -p <pointfile> : specifies a point file name\n" 
	    "-portfile <file> : saves the TCP port number to <file>\n" 
	    "         -server : start the TCP image server\n" 
            "      -simple_pf : simplify pointfiles somewhat\n"
	    "      -stopdebug : not only that, but stops at each message\n" 
	    "              -v : prints version number\n"  
	    "     -wt <title> : sets the window title\n" 
	    "           image : GIF, ICS, JPEG, TIFF, Z-IMAGE or any type supported by ImageMagick.\n" 
	    "\n" 
	    "Other standard X11 options:\n" 
	    "---------------------------\n" 
	    "%s\n",
	    newfname,
	    blankpad,
	    blankpad,
	    blankpad,
	    blankpad,
	    blankpad,
	    newfname,
	    Fl::help);
    return;
}

static void printZusage(char *fname)
{
  fprintf(stderr,
	  "Purpose: displays images under X11\n"
	  "Usage  : %s(<image1>,[cmap:\"std_grey\"],<image2>,[cmap:\"std_grey\"],...\n"
	  "               ,[CMAP:\"std_grey\"],[file:\"pointfile\"],[append:\"F/T\"]\n"
          "               ,[mag:1.0], [xmax:1.0], [ymag:1.0], [gamma:1.0]\n"
	  "               ,[title:\"imview\"], [debug:\"F/T\"])\n"
	  "    <image1>, <image2>, ... is a list of arbitrary images\n"
	  "    cmap:\"some_LUT\" applies a colour LUT only to preceding image \n"
	  "                    on command line\n"
	  "    CMAP:\"some_LUT\" applies a colour LUT to all images on the command\n"
	  "                    line, except for those with a cmap: argument\n"
	  "    file:\"point_file_name\"  names the default file where points\n"
	  "                    location and values can be saved\n"
	  "    append:\"F/T\" appends to an existing pointfile\n"
	  "    mag/xmag/ymag:<xx.xx> changes the default magnification to <xx.xx> \n"
	  "    gamma:<x.xx> changes the default gamma value \n"
	  "    title:\"some_title\" changes the window title \n"
	  "    debug:\"F/T\" turns on some debugging messages\n",
	  myBaseName(fname)
      );

  return;
}

static void printCredits(char *fname)
{
    printf("%s version %s build #%d for %s. Made on %s by %s\n"
	   "Copyright Hugues Talbot, Image Analysis Group, CSIRO-MIS 1997-2003\n",
	   myBaseName(fname),
	   patchlevel,
	   totalbnb,
	   systemName(),
	   __DATE__,
   	   builder
	);
    // more details concerning the configuration
    printf("Configuration:\n");
    printf("-------------------------------+-----\n");
#ifdef HAVE_TIFF
    printf("  - Native TIFF support:  Yes  | \n");
#else
    printf("  - Native TIFF support:       | No\n");
#endif
    printf("-------------------------------+-----\n");
#ifdef HAVE_JPEG
    printf("  - Native JPEG support:  Yes  | \n");
#else
    printf("  - Native JPEG support:       | No\n");
#endif
    printf("-------------------------------+-----\n");
#ifdef HAVE_MAGICK
    printf("  - LibMagick support  :  Yes  | \n");
#else
    printf("  - LibMagick support  :       | No \n");
#endif
    printf("-------------------------------+-----\n");
#ifdef HAVE_PTHREADS
    printf("  - Image server       :  Yes  | \n");
#else
    printf("  - Image server       :       | No \n");
#endif
    printf("-------------------------------+-----\n");
#ifdef HAVE_SYSV_IPC
    printf("  - Unix SYSV IPC      :  Yes  | \n");
#else
    printf("  - Unix SYSV IPC      :       | No \n");
#endif
    printf("-------------------------------+-----\n");
#ifdef HAVE_POSIX_IPC
    printf("  - Unix POSIX IPC     :  Yes  | \n");
#else
    printf("  - Unix POSIX IPC     :       | No \n");
#endif
    printf("-------------------------------+-----\n");
    
    return;
}


// look for lut in given path
static char *clutFullPath(char *lutproto)
{
    int         i = 0;
    static char lutpath[DFLTSTRLEN];
    char       *dotp;

    if ((lutproto != 0) && (extpathlist != 0)) {
	while (extpathlist[i] != 0) {
	    sprintf(lutpath,"%s/%s", extpathlist[i], lutproto);
	    if (((dotp = strrchr(lutpath, '.')) == 0)
		|| (strcmp(dotp, CLUT_SUFFIX) != 0)) {
		// we need to add the .lut suffix
		strcat(lutpath, CLUT_SUFFIX);
	    }
	    if (access(lutpath, R_OK) == 0) {
		dbgprintf("Found lut %s\n", lutpath);
		return lutpath;
	    }
	    i++;
	}
	// if we get there this means we got no valid lut path
	errprintf("%s not a valid colour LUT\n", lutpath);
    }
	
    return 0;
}

// this little function will remove the \" at the end
// of stupid Z-IMAGE string arguments
static char *cleanZarg(char *input)
{
    char *pcbuf;
    int   k = 0;
    
    pcbuf = strdup(input); // the caller will  need to free this later on
    while ((pcbuf[k] != '\0') && (pcbuf[k] != '\"')) 
	k++;
    pcbuf[k] = '\0';

    return pcbuf;
}
    

// argument checking function
// this function may not return at all (calls exit)
// is is a bit too complicated because we try to
// support both the normal Unix command line and the Z-IMAGE
// quirky interface
void checkArgs(int argc, char **argv, char ***imagelist, char ***clutlist, double *defaultZoom, double *defaultGamma)
{
    int    i = 1, nbimages, j = 0, stl;
    int    k, fltk_args;
    bool   checkMinus = true; // check that arguments start with -, when false, even those will be considered images


    // empty the application name
    appName[0] = '\0';
    
    // allocate an image list no matter what
    *imagelist = new char *[argc];
    *clutlist = new char *[argc];
    
    // sensible default
    *defaultZoom = 1.0;
    *defaultGamma = 1.0;

    // empty optional portfile
    portfilename[0] = '\0';

    if (argc > 1) {    
	nbimages = argc - 1;
	// test to see if we are being called from Z-IMAGE
	stl = strlen(argv[1]);
	if (((stl >= 5)   // first part is for Z-IMAGE
	     && (argv[1][0] == 'z')
	     && (argv[1][1] == '_')
	     && (isdigit(argv[1][2]))
	     && (argv[1][stl-2] == '_')
	     && (argv[1][stl-1] == 'z'))
	    || ((strncmp(argv[1], "Zfile", 5) == 0) // second part is for SZ
		&& (argv[1][stl-1] == 'z') && (argv[1][stl-2] == '_')
		&& (isdigit(argv[1][5]))
		) ){
	    dbgprintf("We are being called from Z-IMAGE\n");
	    zimageArgs = 1;
	    i++;
	    nbimages--;
	}    

	while (i < argc) {
	    if ((fltk_args = Fl::arg(argc, argv, i)) != 0) { // test if argument recognized by FL
		nbimages -= fltk_args;
		continue; // i gets correctly incremented by Fl:arg
	    } else if (strncmp(argv[i], "-gamma", 6) == 0) {
		if (i+1 < argc) {
		    dbgprintf("Got new default gamma factor %s\n", argv[i+1]);
		    *defaultGamma = atof(argv[i+1]);
		    i += 2;
		    nbimages -= 2;
		} else {
		    errprintf("Incomplete argument %s\n", argv[i]);
		    // continue anyway
		    i++;
		    nbimages--;
		}
	    } else if (zimageArgs && (strncmp(argv[i], "gamma:", 6) == 0)) {
		char *p = cleanZarg(argv[i]+4);
		dbgprintf("Got new default gamma factor %s\n", p);
		*defaultGamma = atof(p);
		i++;
		nbimages--;
	    } else if ((strncmp(argv[i], "-debug", 6) == 0) // test for debugging
		       || (zimageArgs && (strncmp(argv[i], "debug:\"", 7) == 0)
			   && ((toupper(argv[i][7]) == 'T')
			       || (toupper(argv[i][7]) =='Y') ))) {
		printf("Debug is ON\n");
		debugIsOn = 1;
		
		i++;
		nbimages--;
	    } else if ((strncmp(argv[i], "-stopdebug", 10) == 0) // test for debugging
		       || (zimageArgs && (strncmp(argv[i], "stopdebug:\"", 11) == 0)
			   && ((toupper(argv[i][7]) == 'T')
			       || (toupper(argv[i][7]) =='Y') ))) {
		printf("Press any key after each debugging message\n");
		debugIsOn = 1;
		stopDebug = 1;
		i++;
		nbimages--;
	    } else if (zimageArgs && (strncmp(argv[i], "zdebug:\"", 6) == 0)) {
		// ignore argument
		i++;
		nbimages--;
	    } else if (strcmp(argv[i], "-p") == 0) {	 /*  */
		if (i+1 < argc) {
		    dbgprintf("Got default pointfile name: %s\n", argv[i+1]);
		    PtFileMngr->setPtFileName(argv[i+1]);
		    i += 2;       // we progress by 2 arguments on the command line:
		    nbimages -= 2; // -p and the argument after that.
		} else {
		    errprintf("Incomplete argument %s\n", argv[i]);
		    // but continue anyway...
		    i++;
		    nbimages--;
		}
	    } else if (zimageArgs && (strncmp(argv[i], "file:\"", 6) == 0)) {
		// this strdups the input argument
		char *p = cleanZarg(argv[i]+6);
		dbgprintf("Got default pointfile name: %s as Z argument\n", p);
		PtFileMngr->setPtFileName(p);
		i++;       // we progress by only one argument on the command line:
		nbimages--;
		// free the input argument
		free(p);
	    } else if ((strncmp(argv[i], "-a", 2) == 0) // we want to append data to a pointfile
		       || (zimageArgs && (strncmp(argv[i], "append:\"", 8) == 0)
			   && ((toupper(argv[i][8]) == 'T')
			       || (toupper(argv[i][8]) == 'Y')))) {
		dbgprintf("Pointfile append mode\n");
		appendPoints = 1;
		i++;
		nbimages--;
	    } else if (strncmp(argv[i], "-mag", 4) == 0) {
		if (i+1 < argc) {
		    dbgprintf("Got new default zoom factor %s\n", argv[i+1]);
		    *defaultZoom = atof(argv[i+1]);
		    i += 2;
		    nbimages -= 2;
		} else {
		    errprintf("Incomplete argument %s\n", argv[i]);
		    // continue anyway
		    i++;
		    nbimages--;
		}
	    } else if (zimageArgs && (strncmp(argv[i], "mag:", 4) == 0)) {
		char *p = cleanZarg(argv[i]+4);
		dbgprintf("Got new default zoom factor %s\n", p);
		*defaultZoom = atof(p);
		i++;
		nbimages--;
	    } else if (zimageArgs && ((strncmp(argv[i], "xmag:", 5) == 0)
				      || (strncmp(argv[i], "ymag:", 5) == 0))) {
		char *p = cleanZarg(argv[i]+5);
		dbgprintf("Got new default zoom factor %s\n", p);
		*defaultZoom = atof(p);
		i++;
		nbimages--;
	    } else if (strncmp(argv[i], "-fit", 4) == 0)  {
                displaymodepref = IMV_DISPLAY_IMG_FIT_WINDOW;
                ++i;
                --nbimages;
            } else if (strncmp(argv[i], "-dec", 4) == 0) {
                displaymodepref = IMV_DISPLAY_DECOUPLE_WIN_IMG;
                ++i;
                --nbimages;
            } else if (strncmp(argv[i], "-wt", 3) == 0) {
		if (i+1 < argc) {
		    dbgprintf("Got the window title: %s\n", argv[i+1]);
		    strcpy(appName, argv[i+1]);
		    i += 2;
		    nbimages -= 2;
		} else {
		    errprintf("Incomplete argument %s\n", argv[i]);
		    // continue anyway
		    i++;
		    nbimages--;
		}
	    } else if (zimageArgs && (strncmp(argv[i], "title:\"", 6) == 0)) {
		// this strdups the input argument
		char *p = cleanZarg(argv[i]+7);
		dbgprintf("Got window title: %s as Z argument\n", p);
		strcpy(appName, p);
		i++;       // we progress by only one argument on the command line:
		nbimages--;
		// free the input argument
		free(p);
	    } else if (strncmp(argv[i], "-server", 7) == 0) {
#ifdef HAVE_PTHREADS
		dbgprintf("We shall be starting the server momentarily\n");
		startServer = 1;
#else
		dbgprintf("Server not compiled in\n");
#endif
		i++;
		nbimages--;
	    } else if (strncmp(argv[i], "-servdebug", 10) == 0) {
		dbgprintf("Debugging the server\n");
		serverDebug = 1;
		i++;
		nbimages--;
	    } else if (strncmp(argv[i], "-fork", 5) == 0) {
		dbgprintf("Will fork by itself\n");
		performFork = true;
		i++;
		nbimages--;
	    } else if (strncmp(argv[i], "-ntchild", 8) == 0) {
		dbgprintf("we got restarted by a win32 CreateProcess\n");
		ntChild = true;
		i++;
		nbimages--;
	    } else if (strncmp(argv[i], "-darwinchild", 12) == 0) {
	      dbgprintf("We got restarted by a Darwin/MacOS/Carbon fork()\n");
	      darwinChild=true;
	      i++;
	      nbimages--;
	    } else if (strncmp(argv[i], "-delete", 7) == 0) {
		dbgprintf("We will delete images after use\n");
		deleteAfter = 1;
		i++;
		nbimages--;
	    } else if (strncmp(argv[i], "-portfile", 9) == 0) {
		dbgprintf("Port file is expected as next argument... \a");
		if (i+1 < argc) {
		    dbgprintf("got port file name: %s\b", argv[i+1]);
		    strncpy(portfilename, argv[i+1], PF_MAX_LEN);
		}
		i += 2;
		nbimages -= 2;
	    } else if (strncmp(argv[i], "-no_dblbuf", 9) == 0) {
                dbgprintf("not using offscreen buffer at zoom = 1.0\n");
                useOffscreenBuff = false;
                ++i;
                --nbimages;
            } else if (strncmp(argv[i], "-hide_menubar", 10) == 0) {
                dbgprintf("No main menu bar\n");
                hideMainMenu = true;
                ++i;
                --nbimages;
            } else if (strncmp(argv[i], "-disable_menubar", 16) == 0) {
                dbgprintf("Disabled main menu bar\n");
                disableMainMenu = true;
                ++i;
                --nbimages;
            } else if (strncmp(argv[i], "-disable_io_keys", 16) == 0) {
                dbgprintf("Disabled I/O related key shortcuts\n");
                disableIOKeys = true;
                ++i;
                --nbimages;
            } else if (strncmp(argv[i], "-disable_quit", 13) == 0) {
                dbgprintf("Imview will keep going forever...\n");
                disableQuit = true;
                ++i;
                --nbimages;
            } else if (strncmp(argv[i], "-locked", 7) == 0) {
                dbgprintf("Imview is locked on\n");
                hideMainMenu = true;
                disableMainMenu = true;
                disableIOKeys = true;
                disableQuit = true;
                ++i;
                --nbimages;
            } else if (strncmp(argv[i], "-no_magick", 10) == 0) {
		dbgprintf("Use of ImageMagick is disallowed\n");
		useMagick = false;
		++i;
		--nbimages;
	    } else if (strncmp(argv[i], "-force_magick", 13) == 0) {
                dbgprintf("Use of ImageMagick is forced\n");
		forceMagick = true;
		++i;
		--nbimages;
            } else if (strncmp(argv[i], "-simple_pf", 10) == 0) {
                dbgprintf("Simplify pointfile output\n");
		simplePointfile = true;
		++i;
		--nbimages;
            } else if (strncmp(argv[i], "-sync", 7) == 0) {
                dbgprintf("Synchronizing display\n");
                syncDisplay = true;
                 ++i;
                --nbimages;
            }  else if (strncmp(argv[i], "-v", 2) == 0) {
		printCredits(argv[0]);
		delete[] *imagelist;
		delete[] *clutlist;
		*imagelist = *clutlist = 0;
		imview_exit(0);
	    } else if (strncmp(argv[i], "-C", 2) == 0) {
		if (i+1 < argc) {
		    dbgprintf("Got default colourmap %s\n", argv[i+1]);
		    // ignore the colourmaps for the images up to the
		    // point where -C is declared.
		    for (k = 0 ; k < argc ; k++)
			if ((*clutlist)[k] == 0) {
			    // unix-style arguments are not strdup'ed
			    (*clutlist)[k] = argv[i+1];
			}
		    i += 2;        // we progress by 2 arguments on the command line 
		    nbimages -= 2; // (`-C' and argument after that)
		} else {
		    errprintf("Incomplete argument %s\n", argv[i]);
		    // but continue anyway...
		    i ++;
		    nbimages--;
		}
            } else if (zimageArgs && (strncmp(argv[i], "CMAP:\"", 6) == 0)) {
		dbgprintf("Applying colourmap %s to all images\n", argv[i]+6);
		for (k = 0 ; k < argc ; k++)
		    if ((*clutlist)[k] == 0) {
			// not very efficient but succinct
			(*clutlist)[k] = cleanZarg(argv[i]+6);
		    }
		i++;
		nbimages--; // only one argument gets processed in this case.
		// we don't make j progress because no specific image is involved.
	    } else if (strncmp(argv[i], "-c", 2) == 0) { // colourmaps
		if (i+1 < argc) {
		    dbgprintf("Got CLUT %s\n", argv[i+1]);
		    if (j > 0)
			(*clutlist)[j-1] = argv[i+1];
		    else
			errprintf("Colourmap %s doesn't apply to any image\n", argv[i+1]);
		    i += 2;         // -c
		    nbimages -= 2;  // and `lutname' make 2 arguments...
		} else {
		    errprintf("Incomplete argument %s\n", argv[i]);
		    // but continue anyway...
		    i ++;
		    nbimages--;
		}
	    } else if (zimageArgs && (strncmp(argv[i], "cmap:\"", 6) == 0)) {
		if (j > 0) {
		    if ((*clutlist)[j-1] != 0)
			free((*clutlist)[j-1]); // may have been set up by the CMAP: arg
		    // this will strdup the argument as well
		    (*clutlist)[j-1] = cleanZarg(argv[i]+6); 
		    dbgprintf("Got CLUT %s\n", (*clutlist)[j-1]);
		} else {
		    errprintf("Argument %s doesn't apply to any image\n", argv[i]);
		}
		i++; // only one argument gets processed in the Z case
		nbimages--;
	    } else if (strcmp(argv[i], "-") == 0) {
		// read from stdin
		dbgprintf("Reading from stdin\n");
		if (stdinToTemp() == 0) {
		    dbgprintf("Got image %s\n", imTempFileName);
		    (*imagelist)[j] = imTempFileName;
		    (*clutlist)[j++] = 0; // by default...
		} else {
		    errprintf("Couldn't create temporary file to read from standard input\n");
		    nbimages--;
		}
		i++;
	    } else if (strncmp(argv[i], "-h", 2) == 0) {  
		// ask for some help
		printUnixUsage(argv[0]);
		delete[] *imagelist;
		delete[] *clutlist;
		*imagelist = *clutlist = 0;
		imview_exit(0); // no error
	    } else if (strcmp(argv[i], "--") == 0) { // EXACTLY --
		checkMinus = false; // future arguments will be imaages...
		++i;
		--nbimages;
	    } else if (argv[i][0] == '-') { 
		// unrecognized argument
		stderrprintf("Argument %s unrecognized\n", argv[i]);
		delete[] *imagelist;
		delete[] *clutlist;
		*imagelist = *clutlist = 0;
		imview_exit(10); // this is an error
	    } else {
		// this is just an image name
		dbgprintf("Got image %s\n", argv[i]);
		(*imagelist)[j] = argv[i++];
		(*clutlist)[j++] = 0; // by default
	    }
	}

	// mark the end of the list:
	dbgprintf("Got %d image%c\n", nbimages,(nbimages > 1)?'s':'\0');
	(*imagelist)[nbimages] = 0;
	(*clutlist)[nbimages] = 0;

	if (zimageArgs && (nbimages == 0)) {
	    printZusage(argv[0]);
	    delete[] *imagelist;
	    delete[] *clutlist;
	    *imagelist = *clutlist = 0;
	    imview_exit(10);
	}
    } else {
	(*imagelist)[0] = (*clutlist)[0] = 0;
    }
	
    return;
}


void huntCLUT(const char **clutpathlist)
{
    int            i, j, nbext;
    const char    *q;
    char          *p, fullpath[DFLTSTRLEN];

    dbgprintf("----- LOOKING FOR LUTS -----\n");
    // expand the path list
    for (i = 0, j = 0 ; clutpathlist[i] != 0 ; i++) {
	if (clutpathlist[i][0] != 0) {
	    j++;
	    dbgprintf("Expanding %s\n", clutpathlist[i]);
	    q = clutpathlist[i];
	    while (*q) 
		if (*q++ == PATHSEP)  // : or ,
		    j++;
	}
    }
    nbext = j;
    dbgprintf("total number of paths: %d\n", nbext);
    extpathlist = new char *[nbext+1];
    // get all the starting points
    for (i = 0, j = 0 ; clutpathlist[i] != 0 ; i++) {
	if (clutpathlist[i][0] != 0) {
	    extpathlist[j++] = strdup(clutpathlist[i]);
	    q = clutpathlist[i];
	    while (*q) {
		if (*q++ == PATHSEP)
		    extpathlist[j++] = strdup(q);
	    }
	}
    }
    // list terminated by NULL
    extpathlist[nbext] = 0;

#ifdef WIN32_NOTCYGWIN // Windows way to look through directories
    long               done;
    struct _finddata_t c_file;
    char               fname[DFLTSTRLEN];

    // we force the use of POSIX paths
    for (i = 0 ; i < nbext ; i++) {
	p = strchr(extpathlist[i], PATHSEP);
	if (p) *p = '\0';

	// search this directory
	dbgprintf("Searching %s\n", extpathlist[i]);
	strncpy(fname, extpathlist[i], DFLTSTRLEN);
	// replaces all `\\' with `/'
	for (p = fname; *p != '\0' ; p++)
	    if (*p == '\\')
		*p = '/';
	// add the extension we are looking for
	strcat(fname, "/*.lut");
	if ((done = _findfirst(fname, &c_file)) != -1L) {
	    do {
		dbgprintf("Found: %s\n", c_file.name);
		strncpy(fullpath, extpathlist[i], DFLTSTRLEN-strlen(c_file.name)-1);
		strcat(fullpath, "/");
		strcat(fullpath, c_file.name);
		if (!mainMenuBar->isAlreadyInItemList(fullpath, CLUT_LIST_ID))
		    mainMenuBar->addToItemList(fullpath,CLUT_LIST_ID);
	    } while (_findnext(done, &c_file) == 0);
	}
	_findclose(done);
    }
    
#else // the Unix way
    DIR           *dirp;
    struct dirent *dp;
    char *name, *dotp;

    // get all the endpoints
    for (i = 0 ; i < nbext ; i++) {
	p = strchr(extpathlist[i], PATHSEP);
	if (p) *p = '\0';

	// search this directory
	dbgprintf("Searching %s\n", extpathlist[i]);
	if ((dirp = opendir (extpathlist[i])) == 0) {
	    warnprintf("huntCLUT: cannot access directory %s\n", extpathlist[i]);
	    continue; // skip over that one
	}
	// loop searching for lut files
	for (dp = readdir (dirp); dp != 0; dp = readdir (dirp)) {
	    name = dp->d_name;
	    if ((name != 0)
		&& ((dotp = strrchr(name,'.')) != 0)
		&& (strcmp (dotp,CLUT_SUFFIX) == 0)) {
		dbgprintf("Found: %s\n", name);
		strncpy(fullpath, extpathlist[i],  DFLTSTRLEN-strlen(name)-1);
		strcat(fullpath, "/");
		strcat(fullpath, name);
		if (!mainMenuBar->isAlreadyInItemList(fullpath, CLUT_LIST_ID))
		    mainMenuBar->addToItemList(fullpath,CLUT_LIST_ID);
	    }
	}
	closedir (dirp);
    }
#endif // WIN32_NOTCYGWIN
    
    dbgprintf("----- END OF LOOKING FOR LUTS -----\n");
    
    return;
}


void readUserPrefs(void)
{
    char        prefpath[DFLTSTRLEN];
    char        tmppath[DFLTSTRLEN];
    int         i, j;
    const char  *p;
    
    dbgprintf("----- LOOKING FOR PREFERENCES -----\n");
    // allocate the preferences manager, and initialise it with pref file
    fileprefs = new imprefs;

    currentprefspath[0] = '\0'; // initialize this to empty
    
    // read the user preferences if present
    if (((p = getenv("IMVIEWHOME")) != 0) || ((p = getenv("HOME")) != 0)) {
	strncpy(prefpath, p, DFLTSTRLEN);
        // keep these potential paths as the install path
        snprintf(installpath, DFLTSTRLEN, "%s%c%s", prefpath, PATHSEP, PrefPath);
    } else {
	snprintf(prefpath, DFLTSTRLEN, ".%c%s", PATHSEP, PrefPath); // look in the default installation directory (long shot)
        strncpy(installpath, prefpath, DFLTSTRLEN);
    }

    for ( i = 0, j = 0; ; ) { // ever
	if ((prefpath[i] != PATHSEP) && (prefpath[i] != '\0')) {
	    tmppath[j++] = prefpath[i++];
	} else {
            tmppath[j++]   = SLASH; // end of string
	    tmppath[j] = '\0';
            strcat(tmppath, "preferences");
	    dbgprintf("looking in %s\n", tmppath);
	    if (fileprefs->readprefs(tmppath)) {
		strcpy(currentprefspath, tmppath); // save the location of the prefs file
		break; // found preferences, finished
	    } else { // no joy there, try next string if possible
		if (prefpath[i] != '\0') {
		    j = 0;
		    i++;
		} else
		    break;
	    }
	}
    }
    
    dbgprintf("----- END OF LOOKING FOR PREFERENCES -----\n");
    return;
}
	
    
// display the images given on the command line
// if possible. Otherwise shows a simple banner
void displayImages(char **imageList, char **lutList)
{
    int  i, openRes, displayOK;
    IMAGEPARM *p;
    const char *clutname;

    // try do display the images that were given on the command line,
    // if there are any...
    displayOK = 0;
    if (imageList && imageList[0]) {
	// place the names on the image list
	for (i = 0 ; imageList[i] != 0 ; i++) {
	    mainMenuBar->addToItemList(imageList[i],IMAGE_LIST_ID);
	    // attach LUT if possible
	    p = (IMAGEPARM *)mainMenuBar->getSelectedItemArgument(IMAGE_LIST_ID);
	    clutname = clutFullPath(lutList[i]);
	    if ((p != 0) && (clutname != 0)) {
		if (p->clutpath != 0)
		    free(p->clutpath);
		p->clutpath = strdup(clutname);
		dbgprintf("Attaching clut %s to image %s as default\n",
			  p->clutpath,
			  p->itempath);
	    }
	}
	// display the first valid image
	i = 0;
	displayOK = 1;
	while ((openRes = openimage(imageList[i])) != 0) {
	    // the curre