// ----------------------------------------------------------------------------
//
//  Copyright (C) 2003-2013 Fons Adriaensen <fons@linuxaudio.org>
//    
//  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 3 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, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------


#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include "styles.h"
#include "editwin.h"
#include "global.h"



Editwin::Editwin (X_window *parent, X_callback *callb, int xp, int yp, int xs, int ys) :
    X_window (parent, xp, yp, xs, ys, Colors.plot_bg),
    _callb (callb),
    _xs (xs),
    _ys (ys),
    _x0 (9999),
    _x1 (-9999),
    _show (false),
    _wait (false),
    _xdrag (INT_MIN),
    _mdrag (INT_MIN),
    _cmark (INT_MIN)
{
    int  k, x, y;

    x_add_events (ExposureMask | ButtonPressMask | ButtonReleaseMask | ButtonMotionMask | StructureNotifyMask);  

    _xd = _xs - LSW - RPM - RWW;
    _yd = _ys - TPM - BSH - BWH;

    _plotw = new X_subwin (this, LSW, TPM, _xd, _yd, Colors.plot_bg);
    _plotw->x_add_events (ExposureMask);
    _plotw->x_map ();

    _lswin = new X_subwin (this, 0, 0, LSW, _yd + TPM + 8, Colors.plot_bg);
    _lswin->x_add_events (ExposureMask);
    _lswin->x_map ();

    _bswin = new X_subwin (this, LSW - 8, TPM + _yd, _xd + 16, BSH, Colors.plot_bg);
    _bswin->x_set_win_gravity (SouthWestGravity);
    _bswin->x_add_events (ExposureMask);
    _bswin->x_map ();

    _rbwin = new X_window (this, _xs - RWW, 0, RWW, _ys - BWH, XftColors.main_bg->pixel);
    _rbwin->x_set_win_gravity (NorthEastGravity);
    _rbwin->x_map ();

    _bbwin = new X_window (this, 0, _ys - BWH, _xs, BWH, XftColors.main_bg->pixel);
    _bbwin->x_set_win_gravity (SouthWestGravity);
    _bbwin->x_map ();

    x = _xs - RWW - 240;
    Bst0.size.x = 17;
    Bst0.size.y = 17;
    for (k = 0; k < 2; k++)
    {
        X_ibutton *B;
        B = new X_ibutton (_bbwin, this, &Bst0, x, 3, disp ()->image1515 (X_display::IMG_UP), B_MK0 + k);
        B->x_set_win_gravity (EastGravity);
        B->set_col (Colors.plot_mk [k], XftColors.mark_fg->pixel);
        B->x_map ();
        _marks [k]._flags = 0;
        _marks [k]._color = Colors.plot_mk [k] ^ Colors.plot_bg;
        _marks [k]._butt = B;
        x += 17;
    }
    _tmark = new X_textip (_bbwin, 0, &Tst1, x + 4, 3, 160, 16, 31, 0);
    _tmark->x_set_win_gravity (EastGravity);
    _tmark->x_map ();

    Bst0.size.x = 17;
    Bst0.size.y = 17;
    x = _xs - RWW - 34;
    _bxmi = new X_ibutton (_bbwin, this, &Bst0, x, 3, disp ()->image1515 (X_display::IMG_MI), B_XMI);
    _bxmi->x_set_win_gravity (EastGravity);
    _bxmi->x_map ();
    x += 17;
    _bxpl = new X_ibutton (_bbwin, this, &Bst0, x, 3, disp ()->image1515 (X_display::IMG_PL), B_XPL);
    _bxpl->x_set_win_gravity (EastGravity);
    _bxpl->x_map ();

    Bst0.size.x = 35;
    Bst0.size.y = 18;
    y = 0;
    _blin = new X_tbutton (_rbwin, this, &Bst0, 1, y, "Lin", 0, B_LIN);
    _blin->x_set_win_gravity (NorthGravity);
    _blin->x_map ();
    y += 18;
    _blog = new X_tbutton (_rbwin, this, &Bst0, 1, y, "Log", 0, B_LOG);
    _blog->x_set_win_gravity (NorthGravity);
    _blog->x_map ();

    Bst0.size.x = 17;
    Bst0.size.y = 17;
    y = _ys - BWH - 34;
    _bypl = new X_ibutton (_rbwin, this, &Bst0, 4, y, disp ()->image1515 (X_display::IMG_PL), B_YPL);
    _bypl->x_set_win_gravity (SouthEastGravity);
    _bypl->x_map ();
    y += 17;
    _bymi = new X_ibutton (_rbwin, this, &Bst0, 4, y, disp ()->image1515 (X_display::IMG_MI), B_YMI);
    _bymi->x_set_win_gravity (SouthEastGravity);
    _bymi->x_map ();

    k = disp ()->xsize () - LSW - RPM - RWW + 2;
    _segm = new XSegment [k];
    set_data (0, 0, 0, 0, 0);
}


Editwin::~Editwin (void)
{
    delete[] _segm;
}


void Editwin::handle_event (XEvent *E)
{
    switch (E->type)
    {
    case MapNotify:
        _show = true;
        break;
    case UnmapNotify:
        _show = false;
        break;
    case GraphicsExpose:
    case Expose:
        expose ((XExposeEvent *) E);
        break;  
    case ButtonPress:
        bpress ((XButtonEvent *) E);
        break;  
    case ButtonRelease:
        brelse ((XButtonEvent *) E);
        break;  
    case MotionNotify:
        motion ((XPointerMovedEvent *) E);
        break;  
    }
}


void Editwin::handle_callb (int k, X_window *W, XEvent *E )
{
    int b, c, e;

    c = X_callback::cb_class (k);
    e = X_callback::cb_event (k);
    switch (c)
    {
    case X_callback::BUTTON:
    {
	X_button *B = (X_button *) W;
        b = ((XButtonEvent *) E)->button;
        k = B->cbid ();
        if (e == X_button::PRESS)
	{
	    switch (k)
	    {
	    case B_MK0:
	    case B_MK1:
                if (b == Button3) show_mark (k - B_MK0, 2 * _xd / 5); 
                else goto_mark (k - B_MK0);
		break;
	    case B_XMI:
                set_xzoom ((b == Button3) ? -4 : -1);                
		break;
	    case B_XPL:
                set_xzoom ((b == Button3) ? 4 : 1);                
		break;
	    case B_LIN:
                logmode (false);
        	break;
	    case B_LOG:
                logmode (true);
        	break;
	    case B_YPL:
                set_yzoom (1);
		break;
	    case B_YMI:
                set_yzoom (-1);
		break;
	    }
	}
	break;
    }

    default:
	;
    }
}


void Editwin::expose (XExposeEvent *E)
{
    int x0, x1;

    if (E->window == win ())
    {
        X_draw D (dpy (), win (), dgc (), xft ());
        D.setcolor (Colors.plot_gr);
        D.setfunc (GXcopy);
        D.setline (0);
        D.move (LSW - 1, TPM - 1);
        D.rdraw (_xd + 2, 0);
        D.rdraw (0, _yd + 2);
    }
    else if (E->window == _plotw->win ())
    {
        x0 = E->x;
        x1 = E->x + E->width;
        if (x0 < _x0) _x0 = x0;
        if (x1 > _x1) _x1 = x1;
        if (E->count == 0) redraw ();
    }
    else if (E->window == _bswin->win ())
    {
	if (E->count == 0) plot_xscale ();
    }
    else if (E->window == _lswin->win ())
    {
	if (E->count == 0) plot_yscale ();
    }
}


void Editwin::resize (int xs, int ys)
{
    if ((_xs != xs) || (_ys != ys))
    {
        _xs = xs;
        _ys = ys;
        _xd = _xs - LSW - RPM - RWW;
        _yd = _ys - TPM - BSH - BWH;
        set_yscale ();
        x_resize (_xs, _ys); 
        _plotw->x_resize (_xd, _yd);
        _lswin->x_resize (LSW, _yd + TPM + 8);
        _rbwin->x_resize (RWW, _ys - BWH);
        _bswin->x_resize (_xd + 16, BSH);
        _bbwin->x_resize (_xs, BWH);
	check_marks ();
    }
}


void Editwin::bpress (XButtonEvent *E)
{
    if (E->subwindow == _plotw->win ())
    {
        _mdrag = find_mark (E->x - LSW, E->y - TPM);
        if (_data && (_mdrag < 0) && (E->button == Button3))
	{
            if      (_marks [0]._flags == Mark::OFF) curr_mark (_mdrag = 0);
            else if (_marks [1]._flags == Mark::OFF) curr_mark (_mdrag = 1);
            if (_mdrag >= 0) show_mark (_mdrag, E->x - LSW);
	}
    }
    else if (E->subwindow == _bswin->win ())
    {
        _xdrag = E->x;
    }
}


void Editwin::motion (XPointerMovedEvent *E)
{
    if (_xdrag != INT_MIN)
    {
        if (! _wait)
	{ 
            xscroll (_xdrag - E->x); 
            _xdrag = E->x;
	}
    }
    else if (_mdrag >= 0)
    {
        move_mark (E->x - LSW);
	curr_mark (_cmark);
    }
}


void Editwin::brelse (XButtonEvent *E)
{
    _xdrag = INT_MIN;
    _mdrag = INT_MIN;
    check_marks ();
}


void Editwin::set_data (Impdata *data, int mask, int opts, float t0, float t1)
{
    _data = data;
    if (_data)
    {
        if (mask)
	{
	    _mask = mask;
	    _opts = opts;
	    _blin->set_stat ((opts &  LOGMODE) ? 0 : 2);
	    _blog->set_stat ((opts &  LOGMODE) ? 2 : 0);
	    _rate = (U32) data->drate ();
            _offs = 0;
	    _ind0 = 0;
            _gain = 1.0; 
            _slin = 0;
            _slog = -80;
            set_xrange (t0, t1);
            set_yscale ();
            plot_yscale ();
            clear_marks ();
	}
    }
    else
    {
        _mask = 1;
        _opts = 0;
        _blin->set_stat (0);
        _blog->set_stat (0);
        _rate = 48000;
        _offs = 0;
	_ind0 = 0;
        _gain = 1.0; 
        _slin = 0;
        _slog = -80;
        set_yscale ();
        plot_yscale ();
        set_xrange (0.0f, 1.0f);
        clear_marks ();
    } 
    update ();
}


void Editwin::set_mask (int mask)
{
    if (mask && (_mask != mask))
    {
        _mask = mask;
	set_yscale (); 
        plot_yscale ();
        update ();
    }
}


void Editwin::logmode (bool on)
{
    int b = on ? (_opts | LOGMODE) : (_opts & ~LOGMODE);  
    if (b != _opts)
    {
        _opts = b;
        _blin->set_stat (on ? 0 : 2);
        _blog->set_stat (on ? 2 : 0);
	set_yscale (); 
        plot_yscale ();
        update ();
    }
}


void Editwin::overlay (bool on)
{
    int b = on ? (_opts | OVERLAY) : (_opts & ~OVERLAY);
    if (b != _opts)
    {
        _opts = b;
	set_yscale (); 
        plot_yscale ();
        update ();
    }
}


void Editwin::set_offs (int d)
{
    _offs = d;
    plot_xscale ();
    update ();
}


void Editwin::set_ind0 (int d)
{
    _ind0= d;
    update ();
    curr_mark (_cmark);
}


void Editwin::set_gain (float g)
{
    _gain = g;
    update ();
}


void Editwin::unset_all (void)
{
    _offs = 0;
    _ind0 = 0;
    _gain = 1.0;
    clear_marks ();
    plot_xscale ();
    update ();
}


void Editwin::set_xrange (float t0, float t1)
{
    int    k;
    double t;

    t = (t1 - t0) * _rate / (_xd - 1);
    for (k = 0; (k < NZOOM - 1) && (t > _ztab [k]); k++);
    _zoom = _ztab [_zind = k]; 
    k = (int)(floor (t0 * _rate / _zoom));
    _fst0 = k * _zoom;
    set_xscale ();
    plot_xscale ();
    update ();
    check_marks ();
}     


void Editwin::set_xzoom (int z)
{
    int j, n;

    z = _zind - z;
    if (z >= NZOOM) z = NZOOM - 1;
    else if (z < 0) z = 0;
    if (z != _zind)
    {
        if ((_cmark >= 0) && (_marks [_cmark]._flags != Mark::OFF))
	{
            j = (_marks [_cmark]._posit - _fst0 - _data->tref_i ()) / _zoom;     
            if ((j < 0) || (j >= _xd)) j = _xd / 2;
        }
	else j = _xd / 2;
        n = _fst0 + j * _zoom;
        _zoom = _ztab [_zind = z];
        n = (int)(floor ((double) n / _zoom + 0.5)) * _zoom;
        _fst0 = n - j * _zoom;
        set_xscale ();
        plot_xscale ();
        update ();
        check_marks ();
    }
}


void Editwin::set_yzoom (int d)
{
    if (_opts & LOGMODE)
    {
	d = (d > 0) ? -20 : 20;
        _slog += d;
        if (_slog >  -20) _slog =  -20;
        if (_slog < -160) _slog = -160;
    }
    else
    {
	d = (d > 0) ? -6 : 6;
        _slin += d;
        if (_slin >  12) _slin =  12;
        if (_slin < -120) _slin = -120;
    }
    set_yscale ();
    plot_yscale ();
    update ();
}


void Editwin::set_xscale (void)
{
    int    d;
    double t;


    _xsc = (double) _rate / _zoom;
    _xmm = 5;
    d = 1000000;
    t = 6 * _xsc;
    while (d > 5 * t) d /= 10;
    if (d > 2 * t) { d /= 5;  _xmm = 4; }
    else if (d > t) d /= 2;
    _xun = 100.0 / d;
}


void Editwin::set_yscale (void)
{
    int b, d, i, k, n, y;

    if (_opts & OVERLAY) n = 1;
    else for (i = n = 0, b = _mask; i < NTRACE; b >>= 1, i++) n += b & 1;
    if (n > 4) n = 1;
    k = (_yd - 8) / n;   
    d = 8 + (k & 7);
    k -= d;
    y = 4 + d / 2;
//    if (~_opts & LOGMODE) y += k / 2;
    for (i = 0, b = _mask; i < NTRACE; b >>= 1, i++)
    {
        _ypos [i] = y;
        if ((b & 1) && (n > 1)) y += k + d;
    }
    if (_opts & LOGMODE)
    {
        for (i = 0; i < NYTAB; i++)
        {
	    d = _ytab [i];
            if (k * d / -_slog >= 30) break;
	}
        if (i == NYTAB) d = -_slog;
    }
    else
    {
	d = k / 2;
	if (d > 60) d /= 2;
	if (d > 60) d /= 2;
    }
    _yref = k;         
    _ydel = d;
}


void Editwin::plot_xscale (void)
{
    int         i, d, x, x0;
    double      t, t0;;
    char        s [16];
    const char  *f;
    X_draw      D (dpy (), _bswin->win (), dgc (), xft ());

    if (!_show) return;

    D.setfont (XftFonts.scales);
    D.clearwin ();
    D.setfunc (GXcopy);
    D.setline (0);
    x0 = 8;
    t0 = (double)(_fst0 - _offs) / _rate;
    d = XftFonts.scales->ascent + 2;
    f = (_xun > 0.019f) ? "%3.1lf" : "%5.3lf";
    i = _xi0 = (int)(ceil (t0 / _xun));

    D.setcolor (Colors.plot_gr);
    D.move (x0 - 1, 0);
    D.rdraw (_xd + 2, 0);
    D.setcolor (XftColors.plot_sc);

    while (1)
    {
	t = i * _xun;
	x = (int)((t - t0) * _xsc + 0.5);
        if (x >= _xd) break;
        D.move (x + x0, 0);
        if (i % _xmm) D.rdraw (0, 3);
        else
	{
	    sprintf (s, f, t);
            D.rdraw (0, 5);
            D.rmove (0, d);
            D.drawstring (s, 0);
	}
        i++;
    }
}


void Editwin::plot_yscale (void)
{
    int         b, d, i, j, x0, y, y0, dy;
    float       u;
    char        s [16];
    X_draw      D (dpy (), _lswin->win (), dgc (), xft ());

   if (!_show) return;

    D.setfont (XftFonts.scales);
    D.clearwin ();
    D.setfunc (GXcopy);
    D.setline (0);
    x0 = LSW - 1;
    y0 = TPM;
    d = XftFonts.scales->ascent / 2 - 1;

    D.setcolor (Colors.plot_gr);
    D.move (x0, y0 - 1);
    D.rdraw (0, _yd + 2);
    D.setcolor (XftColors.plot_sc);

    if (_opts & LOGMODE)
    {
        u = (float) _yref / -_slog;
    }
    else
    {
	u = 0;
        sprintf (s, "%d", _slin);
    }
    for (i = 0, b = _mask; i < NTRACE; b >>= 1, i++)
    {
	if (b & 1)
	{
            j = 0;
            y = y0 + _ypos [i];
            if (_opts & LOGMODE)
	    {
		while (1)
		{
                    dy = (int)(j * _ydel * u + 0.5f);
                    if (dy > _yref) break;
                    D.move (x0, y + dy);
                    D.rdraw (-5, 0);
                    sprintf (s, "%d", -j * _ydel);
                    D.rmove (-4, d);
                    D.drawstring (s, 1);
                    j++;
		}
	    }
            else
	    {
		while (1)
		{
                    dy = j * _ydel;
                    if (dy > _yref) break;
                    D.move (x0, y + dy);
                    D.rdraw (-5, 0);
		    if (j == 0)
		    {
                        D.rmove (-4, d);
                        D.drawstring (s, 1);
		    }
                    j++;
		}
	    }
	}
    }
}


void Editwin::update (void)
{
    if (_show) 
    {
        _x0 = 0;
	_x1 = _xd;
	redraw ();
    }
}


void Editwin::xscroll (int dx)
{
    X_draw  D (dpy (), _plotw->win (), dgc (), xft ());
    if (dx)
    {
        _wait = true;
        _fst0 += dx * _zoom;  
        plot_xscale ();    
        D.movepix (dx, 0, _xd, _yd);
    }
}


void Editwin::redraw ()
{
    X_draw  D (dpy (), _plotw->win (), dgc (), xft ());

    D.setcolor (Colors.plot_bg);
    D.setfunc (GXcopy);
    D.fillrect (_x0, 0, _x1, _yd);
    D.setclip (_x0, 0, _x1, _yd);
    plot_grid (&D);
    if (_data)
    {
	plot_data (&D);
	D.setfunc (GXxor);
	plot_mark (&D, 0);
	plot_mark (&D, 1);
    }
    D.noclip ();
    _wait = false;
    _x0 =  9999;
    _x1 = -9999;
}


void Editwin::plot_grid (X_draw *D)
{
    int     b, i, j, x, y, dy;
    float   u;
    double  t, t0;

    D->setline (0);
    D->setcolor (Colors.plot_gr);

    t0 = (double)(_fst0 - _offs) / _rate;
    i = _xi0;
    while (1)
    {
        if (i % _xmm == 0)
	{
	    t = i * _xun;
	    x = (int)((t - t0) * _xsc + 0.5);
            if (x >= _xd) break;
            D->move (x, 0);
            D->rdraw (0, _yd);
	}
	i++;
    }

    u = (_opts & LOGMODE) ? ((float) _yref / -_slog) : 1.0f;
    for (i = 0, b = ~_mask; i < NTRACE; b >>= 1, i++)
    {
	if (b & 1) continue;
	y = _ypos [i];
        j = 0;
        while (1)
 	{
	    dy = (int)(j++ * _ydel * u + 0.5f);
            if (dy > _yref) break;  
   	    D->move (0, y + dy);
	    D->rdraw (_xd, 0);
	}
    }
}


void Editwin::plot_data (X_draw *D)
{
    int       b, c, i, j, k, n, x0, x1, y0, r0, r1;
    float     s, v, d, *p, min, max;
    XSegment  *S;
    XPoint    *P;

    if (_opts & LOGMODE)
    {
        s = ((_opts & POWMODE) ? 10.0f : 20.0f) * _yref / _slog;
        d = log10f (_gain);;
    } 
    else 
    {
        s = 0.5f * _yref * _gain * powf (10.0f, -0.05f * _slin);
        d = 0;
    }

    c = _data->n_chan ();
    for (i = 0, b = ~_mask; i < (int)(_data->n_chan ()); b >>= 1, i++)
    {
	if (b & 1) continue;
        D->setcolor (Colors.plot_da [i & 1]);

	y0 = _ypos [i];
	if (~_opts & LOGMODE) y0 += _yref / 2;
        x0 = _x0;
        x1 = _x1;   
        n = x1 - x0;
        k = _fst0 + _data->tref_i () - _zoom / 2; 
        p = _data->data (i);
        r0 = 0;
        r1 = _data->n_fram ();
 
	if (_opts & LOGMODE) 
	{
            P = (XPoint *) _segm;
	    x0--;
            x1++;
            n += 2;
            k += x0 * _zoom;
            while (x0 < x1)
	    {
                if ((k >= r0) && (k + _zoom <= r1)) 
		{
		    max = fabsf (p [c * k++]) + 1e-30;
                    for (j = 1; j < _zoom; j++, k++)
                    {
  		        v = fabs (p [c * k]);
                        if (v > max) max = v;
		    }                    
                    j = (int)(s * (d + log10f (max)) + 0.5f);
                    if (j > _yref + 1) j = _yref + 1;
 		}
		else if ((k + _zoom <= r0) || (k >= r1))
                {
                    j = _yref + 1;
 		    k += _zoom;
		}
		else
		{
		    max = 1e-30f;
                    for (j = 0; j < _zoom; j++, k++)
                    {
			if ((k >= r0) && (k < r1))
                        {
			    v = fabsf (p [c * k]);
                            if (v > max) max = v;
                        }      
		    }
                    j = (int)(s * (d + log10f (max)) + 0.5f);
                    if (j > _yref + 1) j = _yref + 1;
		}		
		P->x = x0++;
                P->y = y0 + j; 
                P++;
	    }
            D->drawlines (n, (XPoint *) _segm);
	}
	else if (_zoom > 1)
	{
            S = _segm;
            k += x0 * _zoom;
            while (x0 < x1)
	    {
                if ((k >= r0) && (k + _zoom < r1)) 
		{
		    max = min = p [c * k];
                    for (j = 1; j <= _zoom; j++)
                    {
  		        v = p [c * ++k];
                        if (v > max) max = v;
                        if (v < min) min = v;
		    }
 		}
		else if ((k + _zoom < r0) || (k >= r1))
                {
                    max = min = 0;
 		    k += _zoom;
		}
		else
		{
		    max = min = 0;
                    for (j = 0; j <= _zoom; j++, k++)
                    {
			if ((k >= r0) && (k < r1))
                        {
			    v = p [c * k];
                            if (v > max) max = v;
                            if (v < min) min = v;
                        }      
		    }
                    --k;
		}		
		S->x1 = S->x2 = x0++;
		S->y1 = y0 - (int) floorf (s * max + 0.5);
		S->y2 = y0 - (int) floorf (s * min + 0.5);
                S++;
	    }
            D->drawsegments (n, _segm);
	}
        else
	{
            P = (XPoint *) _segm;
	    x0--;
            x1++;
            n += 2;
            k += x0;
            while (x0 < x1)
	    {
                if ((k >= r0) && (k < r1)) j = (int) floorf (s * p [c * k++] + 0.5f);
		else { j = 0; k++; }
		P->x = x0++;
                P->y = y0 - j; 
                P++;
	    }
            D->drawlines (n, (XPoint *) _segm);
	}
    }    
}


void Editwin::clear_marks (void)
{
    int     i;
    Mark    *M;
    X_draw  D (dpy (), _plotw->win (), dgc (), xft ());
 
    D.setcolor (Colors.plot_bg);
    D.setfunc (GXxor);
    for (i = 0, M = _marks; i < NMARK; i++, M++)
    {
        plot_mark (&D, i);
        M->_flags = 0;
        M->_butt->set_image (disp ()->image1515 (X_display::IMG_PT));
        M->_butt->set_stat (0);
    }
    curr_mark (_mdrag = INT_MIN);
} 


void Editwin::check_marks (void)
{
    int    i, f, x;
    Mark   *M;

    for (i = 0, M = _marks; i < NMARK; i++, M++)
    {
        f = Mark::OFF;
	if (M->_flags != Mark::OFF)
	{
	    x = (M->_posit - _fst0 - _data->tref_i ()) / _zoom;     
	    if      (x < 0)     f = Mark::LL;
	    else if (x >= _xd)  f = Mark::RR;
            else                f = Mark::ON;
	}
	if (f != M->_flags)
	{
	    switch (M->_flags = f)
	    {
	    case Mark::OFF:
                M->_butt->set_image (disp ()->image1515 (X_display::IMG_PT));
                M->_butt->set_stat (0);
		break;
	    case Mark::ON:
                M->_butt->set_image (disp ()->image1515 (X_display::IMG_UP));
                M->_butt->set_stat (4);
		break;
	    case Mark::LL:
                M->_butt->set_image (disp ()->image1515 (X_display::IMG_LT));
                M->_butt->set_stat (4);
		break;
	    case Mark::RR:
                M->_butt->set_image (disp ()->image1515 (X_display::IMG_RT));
                M->_butt->set_stat (4);
		break;
	    }
            M->_butt->redraw (); 
	}        
    }
} 


void Editwin::show_mark (int i, int x)
{
    Mark *M = _marks + i;

    if (_data)
    {
	if (M->_flags == Mark::ON)
	{
	    togg_mark (i);
	    M->_flags = Mark::OFF;
            M->_butt->set_image (disp ()->image1515 (X_display::IMG_PT));
            M->_butt->set_stat (0);
            if (_cmark == i)
	    {
		M = _marks + (i ^ 1);
		curr_mark ((M->_flags == Mark::OFF) ? INT_MIN : i ^ 1);
	    }
	}
	else
	{
	    M->_flags = Mark::ON;
            M->_posit = _fst0 + _data->tref_i () + x * _zoom;
            M->_butt->set_image (disp ()->image1515 (X_display::IMG_UP));
            M->_butt->set_stat (4);
	    togg_mark (i);
            curr_mark (i); 
	}
    }
}


void Editwin::move_mark (int x)
{
    togg_mark (_cmark);
    _marks [_cmark]._posit = _fst0 + _data->tref_i () + x * _zoom;  
    togg_mark (_cmark);
}


void Editwin::goto_mark (int i)
{
    Mark *M = _marks + i;
 
    if (_data)
    {
        if (M->_flags != Mark::OFF)
        {
	    xscroll ((M->_posit - _fst0 - _data->tref_i ()) / _zoom - _xd / 2);
            curr_mark (i);
            check_marks ();
	}
    }
}


int Editwin::find_mark (int x, int y)
{
    int  i, mx, my;
    Mark *M;

    if (_data)
    {
	for (i = 0, M = _marks; i < NMARK; i++, M++)
	{
	    if (M->_flags != Mark::OFF)
	    {
		mx = (M->_posit - _fst0 - _data->tref_i ()) / _zoom;     
		my = 20 * (i + 1);
		if ((abs (mx - x) < 6) && (abs (my - y) < 6))
		{
		    curr_mark (i);
		    return i;
		}
	    }
	}
    }
    return -1;
}    


void Editwin::togg_mark (int i)
{
    X_draw  D (dpy (), _plotw->win (), dgc (), xft ());
    D.setfunc (GXxor);
    plot_mark (&D, i); 
} 


void Editwin::curr_mark (int i)
{
    char  s [32];
    Mark  *A, *B;

    if (i < 0)
    {
	if (_cmark != i)
	{
            _tmark->set_text ("");
	    _tmark->set_color (Tst1.color.normal.bgnd, XftColors.mark_fg);
	}
    }
    else
    {
        A = _marks + i;
        B = _marks + (i ^ 1);
        if (B->_flags == Mark::OFF) sprintf (s, "%9d", A->_posit);
        else sprintf (s, "%9d   (%+d)", A->_posit - _ind0, A->_posit - B->_posit);
	_tmark->set_text (s);
	if (_cmark != i) _tmark->set_color (Colors.plot_mk [i], XftColors.mark_fg);
    }
    _cmark = i;
}


void Editwin::plot_mark (X_draw *D, int i)
{
    int   x, y;
    Mark  *M = _marks + i; 

    if (M->_flags != Mark::OFF)
    {
	 x = (M->_posit - _fst0 - _data->tref_i ()) / _zoom;     
         if ((x >= -4) && (x < _xd + 4))
	 { 
	     D->setcolor (M->_color);
	     y = 20 * (i + 1);
	     D->move (x, 0);
	     D->draw (x, _yd);
	     D->drawrect (x - 4, y - 4, x + 4, y + 4);    
	     D->drawrect (x - 5, y - 5, x + 5, y + 5);
	 }
    }    
}     


void Editwin::addtext (X_window *W, int xp, int yp, int xs, int ys, const char *text, int align)
{
    (new X_textln (W, &Tst0, xp, yp, xs, ys, text, align))->x_map ();
}


int Editwin::_ztab [NZOOM] = { 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, 1024, 1536, 2048 };
int Editwin::_ytab [NYTAB] = { 1, 2, 5, 10, 15, 20, 30, 40, 60, 80, 120, 160 };


