/*
 * CDE - Common Desktop Environment
 *
 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
 *
 * These libraries and programs are free software; you can
 * redistribute them and/or modify them under the terms of the GNU
 * Lesser General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * These libraries and programs are distributed in the hope that
 * they will be useful, but WITHOUT ANY WARRANTY; without even the
 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 * PURPOSE. See the GNU Lesser General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with these libraries and programs; if not, write
 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
 * Floor, Boston, MA 02110-1301 USA
 */
/* $XConsortium: graphics.c /main/4 1995/11/02 14:05:07 rswiston $ */
/*********************************************************************
*  (c) Copyright 1993, 1994 Hewlett-Packard Company
*  (c) Copyright 1993, 1994 International Business Machines Corp.
*  (c) Copyright 1993, 1994 Sun Microsystems, Inc.
*  (c) Copyright 1993, 1994 Unix System Labs, Inc., a subsidiary of
*      Novell, Inc.
**********************************************************************/
/******************************************************************************
 **  Program:           dticon
 **
 **  Description:       X11-based multi-color icon editor
 **
 **  File:              graphics.c, which contains the following subroutines or
 **                     functions:
 **                       Flicker_Arc()
 **                       Circle_Box()
 **                       Set_HotBox_Coords()
 **                       Start_HotBox()
 **                       Do_HotBox()
 **                       Stop_HotBox()
 **
 ******************************************************************************
 **
 **  Copyright Hewlett-Packard Company, 1990, 1991, 1992.
 **  All rights are reserved.  Copying or reproduction of this program,
 **  except for archival purposes, is prohibited without prior written
 **  consent of Hewlett-Packard Company.
 **
 **  Hewlett-Packard makes no representations about the suitibility of this
 **  software for any purpose.  It is provided "as is" without express or
 **  implied warranty.
 **
 ******************************************************************************/
#include <stdlib.h>
#include <math.h>
#include <Xm/Xm.h>
#include "externals.h"
#include "utils.h"
#include "process.h"

extern GC scratch_gc;

extern Widget editMenu_cut_pb;
extern Widget editMenu_copy_pb;
extern Widget editMenu_rotate_pb;
extern Widget editMenu_flip_pb;
extern Widget editMenu_scale_pb;

extern void Stop_HotBox(void);

/***************************************************************************
 *                                                                         *
 * Routine:   Flicker_Arc                                                  *
 *                                                                         *
 * Purpose:   Given 2 points (top-left and bottom-right), draw an          *
 *            invertable ellipse around the box they form.                 *
 *                                                                         *
 *X11***********************************************************************/

void
Flicker_Arc(
        Window win,
        int x1,
        int y1,
        int x2,
        int y2 )
{
  int x, y, width, height;

  x = min(x1, x2);
  y = min(y1, y2);
  width  = abs(x1 - x2);
  height = abs(y1 - y2);

#ifdef DEBUG
  if (debug)
    stat_out("Doing Flicker_Arc: x=%d, y=%d, width=%d, height=%d\n",
                x, y, width, height);
#endif

  if ((width > 0) && (height > 0))
    XDrawArc(dpy, win, Flicker_gc, x, y, width, height, 0, 360*64);
}


/***************************************************************************
 *                                                                         *
 * Routine:   Circle_Box                                                   *
 *                                                                         *
 * Purpose:   Given 2 points (the center and radius of a circle)           *
 *            generate a box which would exactly enclose this circle,      *
 *            and then draw a flickering circle that matches the box.      *
 *            WARNING: [x1,y1] are always assumed to be the centerpoint    *
 *            and this routine will generate bogus results if this is      *
 *            not TRUE.                                                    *
 *                                                                         *
 *X11***********************************************************************/

void
Circle_Box(
        Window win,
        int x1,
        int y1,
        int x2,
        int y2,
        XRectangle *box )
{
  int radius, top_x, top_y, bottom_x, bottom_y, width, height;
  double size;

#ifdef DEBUG
  if (debug)
    stat_out("Entering Circle_Box\n");
#endif

  width  = mag(x1, x2);
  height = mag(y1, y2);
  size   = (double) ((width * width) + (height * height));
  radius = (int) sqrt(size);

  top_x = x1 - radius;
  top_y = y1 - radius;
  bottom_x  = x1 + radius;
  bottom_y  = y1 + radius;

#ifdef DEBUG
  if (debug)
    stat_out("  Circle_Box values: tx=%d, ty=%d, bx=%d, by=%d\n",
                        top_x, top_y, bottom_x, bottom_x);
#endif

  Flicker_Arc(win, top_x, top_y, bottom_x, bottom_y);

  box->x = top_x;
  box->y = top_y;
  box->width  = bottom_x - top_x + 1;
  box->height = bottom_y - top_y + 1;
#ifdef DEBUG
  if (debug)
    stat_out("Leaving Circle_Box\n");
#endif
}


/***************************************************************************
 *                                                                         *
 * Routine:   Set_HotBox_Coords                                            *
 *                                                                         *
 * Purpose:   A SELECT operation has just occurred.  Initiate a timer      *
 *            calculate the area to enclose.                               *
 *            not TRUE.                                                    *
 *                                                                         *
 *X11***********************************************************************/

#define FLASH_INTERVAL    300
static Boolean FlashState=False;
static int flash_x, flash_y, flash_width, flash_height;
static int box_x1, box_y1, box_x2, box_y2;
static XtIntervalId selectTimerID;
static void Do_HotBox();

void
Set_HotBox_Coords( void )
{
  int min_x, min_y, max_x, max_y, tmp_x, tmp_y;

  min_x = min(ix, last_ix);
  min_y = min(iy, last_iy);
  max_x = max(ix, last_ix);
  max_y = max(iy, last_iy);

/*** make sure all four points are on the tablet ***/

  if (min_x < 0)
    min_x = 0;
  if (min_y < 0)
    min_y = 0;
  if ((max_x) >= icon_width)
    max_x = icon_width-1;
  if ((max_y) >= icon_height)
    max_y = icon_height-1;

  select_box.x = min_x;
  select_box.y = min_y;
  select_box.width  = max_x - min_x + 1;
  select_box.height = max_y - min_y + 1;

  box_x1 = min_x;
  box_y1 = min_y;
  box_x2 = max_x+1;
  box_y2 = max_y+1;

  Tablet_Coords(min_x, min_y, &flash_x, &flash_y);
  Tablet_Coords(max_x+1, max_y+1, &tmp_x, &tmp_y);
  flash_width  = tmp_x - flash_x;
  flash_height = tmp_y - flash_y;

#ifdef DEBUG
  if (debug) {
    stat_out("  select_box: x=%d, y=%d, width=%d, height=%d\n",
                select_box.x, select_box.y,
                select_box.width, select_box.height);
    stat_out("  flash box: x=%d, y=%d, width=%d, height=%d\n",
                flash_x, flash_y, flash_width, flash_height);
   }
#endif
}


/***************************************************************************
 *                                                                         *
 * Routine:   Start_HotBox                                                 *
 *                                                                         *
 * Purpose:   A SELECT operation has just occurred.  Initiate a timer      *
 *            which flashes a 1-pixel wide box around the perimeter of     *
 *            the selected rectangle every FLASH_INTERVAL milliseconds.    *
 *            Use the global variables ix, iy, last_ix, last_iy to         *
 *            calculate the area to enclose.                               *
 *                                                                         *
 *X11***********************************************************************/

void
Start_HotBox(
        int flag )
{

#ifdef DEBUG
  if (debug)
    stat_out("Entering Start_HotBox\n");
#endif

  Selected = True;
  /* turn on stuff that uses the selected area */
  XtSetSensitive( editMenu_cut_pb, True);
  XtSetSensitive( editMenu_copy_pb, True);
  XtSetSensitive(editMenu_rotate_pb, True);
  XtSetSensitive(editMenu_flip_pb,  True);
  XtSetSensitive(editMenu_scale_pb,  True);
  XSync(dpy, 0);
  if (flag == INITIAL)
    Set_HotBox_Coords();

  selectTimerID = XtAppAddTimeOut(AppContext,
                                  FLASH_INTERVAL,
                                  (XtTimerCallbackProc) Do_HotBox,
                                  NULL);

#ifdef DEBUG
  if (debug)
    stat_out("Leaving Start_HotBox - TimerID=%d\n", selectTimerID);
#endif

}


/***************************************************************************
 *                                                                         *
 * Routine:   Do_HotBox                                                    *
 *                                                                         *
 * Purpose:   Flash one alternating pulse around the selected area, and    *
 *            then re-set itself to activate again in FLASH_INTERVAL       *
 *            milliseconds.                                                *
 *                                                                         *
 * note: Check selectTimerID so that timeouts added for previous selects   *
 *       are ignored.  ex: if new select is started before previous select *
 *       timeout is serviced, "Selected" will already be set to true again *
 *       (for new select) when timeout from old selection is called... so  *
 *       now Stop_HotBox is called immediately (before HotBox Coords are   *
 *       set for new select), and last timeout is ignored.                 *
 *                                                                         *
 *X11***********************************************************************/

static void
Do_HotBox(
        XtPointer *client_data,
        XtIntervalId *local_id )
{

#ifdef DEBUG
  if (debug)
    stat_out(". ");
#endif

  if (GraphicsOp != SELECT)
    Selected = False;
  if (*local_id == selectTimerID)
  {
    if (Selected)
    {
      if (FlashState) {
        FlashState = False;
        XSetForeground(dpy, scratch_gc, black_pixel);
      }
      else
      {
        FlashState = True;
        XSetForeground(dpy, scratch_gc, white_pixel);
      }
    XSetLineAttributes(dpy, scratch_gc, 1, LineSolid, CapButt, JoinMiter);
    XDrawRectangle(dpy, tablet_win, scratch_gc,
        flash_x, flash_y, flash_width, flash_height);
    selectTimerID=XtAppAddTimeOut(AppContext,
                                  FLASH_INTERVAL,
                                  (XtTimerCallbackProc) Do_HotBox,
                                  NULL);
    }
    else
      Stop_HotBox();
  }
}


/***************************************************************************
 *                                                                         *
 * Routine:   Stop_HotBox                                                  *
 *                                                                         *
 * Purpose:   Undo the last Selected border operation.                     *
 *                                                                         *
 *X11***********************************************************************/

void
Stop_HotBox( void )
{
  int min_x, min_y, max_x, max_y, tmp_x, tmp_y;
  static int tmp_ix, tmp_iy;
  static Boolean Rotate_Move=False;

#ifdef DEBUG
  if (debug)
    stat_out("Entering Stop_HotBox\n");
#endif

  if (GridEnabled) {
    XDrawLine(dpy, tablet_win, Grid_gc,
                flash_x, flash_y, (flash_x+flash_width), flash_y);
    XDrawLine(dpy, tablet_win, Grid_gc,
                flash_x, (flash_y+flash_height), (flash_x+flash_width),
                (flash_y+flash_height));
    XDrawLine(dpy, tablet_win, Grid_gc,
                flash_x, flash_y, flash_x, (flash_y+flash_height));
    XDrawLine(dpy, tablet_win, Grid_gc,
                (flash_x+flash_width), flash_y, (flash_x+flash_width),
                (flash_y+flash_height));
   }
  else {
    /* since Rotate left and right moves ix and iy revert to tmp_ix, tmp_iy */
    if (Rotate_Move) { Rotate_Move = False;
                       ix = tmp_ix;
                       iy = tmp_iy; }
    min_x = min(ix, last_ix);
    min_y = min(iy, last_iy);
    max_x = max(ix, last_ix);
    max_y = max(iy, last_iy);
    if (++max_x >= icon_width) max_x--;
    if (++max_y >= icon_height) max_y--;
    Transfer_Back_Image(min_x, min_y, max_x, max_y, HOLLOW);
    /* if it is a Rotate Op. then keep ix, iy */
    if(GraphicsOp == S_ROTATE) { Rotate_Move = True;
                                 tmp_ix = ix;
                                 tmp_iy = iy; }
   }

#ifdef DEBUG
  if (debug)
    stat_out("Leaving Stop_HotBox\n");
#endif

}
