/*====================================================
                      ARTemis
                   (version 1.3)
             FM-TOWNS 用ペイントツール

                 by 松内 良介 1994
====================================================*/
#define MODULE_IMGWIN
/*
	imgwins.c

	int	imageWINfunc(kobj, messId, argc, pev, trigger)
	int	imageSBARfunc(kobj, messId, argc, pev, trigger)
	int	ImageEraseDBtnProc(int kobj)

static int draw_line(IMWIN *win, POINT *first, int type)
static int draw_freeline(IMWIN *win, POINT *first, int type)
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winb.h>
#include <te.h>
#include <fntb.h>
#include <gui.h>
#include <egb.h>
#include <wgb.h>
#include <msdos.cf>
#include "art.h"
#include "guisub.h"
#include "wgbmac.H"
#include "alert.h"

#include "imagewin.h"
#include "imgwin.h"
#include "pensel.h"
#include "colsel.h"
#include "fifo.h"
#include "subgrp.h"
#include "desktop.h"
#include "copy.h"

int	idImageWin = -1 ;
int	idImageHSBar = -1 ;
int	idImageVSBar = -1 ;
int	idImageTitleDBtn = -1 ;
int	idImageTitleMsg = -1 ;
int	idImageEraseDBtn = -1 ;
int	idImageBtn = -1 ;

/*--------------------------------------------------------*/
/*                   部品の呼び出し関数                   */
/*--------------------------------------------------------*/

static	int		draw_freeline(IMWIN *win, EVENT* first_ev, int type);
static	int		draw_line(IMWIN *win, POINT *first, int type);

  /* ■画像ウィンドウの呼び出し関数■ */

		/*	initDataIMGWIN:imageWIN:MJ_WINDOWL40の呼び出し関数	*/
	int	imageWINfunc(kobj, messId, argc, pev, trigger)
	int		kobj ;
	int		messId ;
	int		argc ;
	EVENT	*pev ;
	int		trigger ;
	{
		IMWIN *win;
		WINCLIP *clipStack;
		win = imagewin_getWinFromPart(kobj);
		if (win == NULL)
			return -1;

	  /* MM_SHOW メッセージへの応答 */
		if (messId == MM_SHOW)
			imagewin_updateScr(win);

	  /* MM_UPDATE(リサイズ)への応答 */
		else if (messId == MM_UPDATE)
			imagewin_resize(win, TRUE);

	  /* アクティブになった時 */
		else if (messId == MM_WAKE)
		{
			HYPER Ht;
			WINCLIP *clipStack;
			OBJHYP(win->titlebar).clr.back = DARKGRAY;
			OBJHYP(win->titlemsg).clr.ch   = WHITE;
			OBJHYP(win->erasebtn).clr.back = DARKGRAY;
			WIN_beginUpDateObj(win->titlebar, &clipStack);
			MMI_SendMessage(win->titlebar, MM_SHOW, 0);
			MMI_SendMessage(win->titlemsg, MM_SHOW, 0);
			MMI_SendMessage(win->erasebtn, MM_SHOW, 0);
			WIN_endUpDateObj(clipStack);
			imagewin_setCurrentWin(win);
		}

	  /* ノンアクティブになった時 */
		else if (messId == MM_SLEEP)
		{
			HYPER Ht;
			WINCLIP *clipStack;
			OBJHYP(win->titlebar).clr.back = GRAY;
			OBJHYP(win->titlemsg).clr.ch   = BLACK;
			OBJHYP(win->erasebtn).clr.back = GRAY;
			WIN_beginUpDateObj(win->titlebar, &clipStack);
			MMI_SendMessage(win->titlebar, MM_SHOW, 0);
			MMI_SendMessage(win->titlemsg, MM_SHOW, 0);
			MMI_SendMessage(win->erasebtn, MM_SHOW, 0);
			WIN_endUpDateObj(clipStack);
			if (imagewin_getCurrentWin() == win)
				imagewin_setCurrentWin(NULL);
		}

	  // マウスボタンに対する応答
		else if (messId == MM_MOUSEON && (pev->shift&SLEFTBTN) != 0)
		{
			int ret;
			int tool = toolbox_getCurrentTool();
			switch (tool)
			{
				case TOOL_FPSET:
				case TOOL_FLINE:
				case TOOL_KOSURI:
				case TOOL_PAINT:
				case TOOL_NIZIMI:
				case TOOL_BOKASI:
				case TOOL_SAND:
					ret = draw_freeline(win, pev, tool);
					break;
				case TOOL_LINE:
					ret = draw_line(win, (POINT*)&pev->info, 0);
					break;
				case TOOL_BOXFILL:
					ret = draw_line(win, (POINT*)&pev->info, 2);
					break;
				case TOOL_COPY:
					ret = input_rectarea(win, pev);
					break;
				case TOOL_POLYGONCOPY:
					ret = input_polygonarea(win,pev);
					break;
				case TOOL_POLYGONFILL:
					ret = cmd_polygonfill(win,pev);
					break;
				case TOOL_ZOOMCOPY:
					ret = cmd_rectzoomcopy(win,pev);
					break;
			}
			return ret;
		}
		
		else if (messId == MM_MOUSEON && (pev->shift&SRIGHTBTN) != 0)
		{
			// スポイト操作
			POINT mospt = *(POINT*)&pev->info;
			if (imagewin_PtInPic(win,&mospt))
			{
				POINT ptPic;
				imagewin_getPosFromMos(win,&mospt,&ptPic);
				PIXEL pix;
				pic_getPixelXy(win->pic, ptPic.x,ptPic.y, &pix);
				colsel_setCurPixel(&pix);
			}
		}
		return NOERR ;
	}

  /* ■画像ウィンドウのスクロールバー■ */

		/*	initDataIMGWIN:imageSBARhori:MJ_SCRLL40の呼び出し関数	*/
		/*	initDataIMGWIN:imageSBARvert:MJ_SCRLL40の呼び出し関数	*/
	int	imageSBARfunc(kobj, messId, argc, pev, trigger)
	int		kobj ;
	int		messId ;
	int		argc ;
	EVENT	*pev ;
	int		trigger ;
	{
		IMWIN *win;
		if ((win = imagewin_getWinFromPart(kobj)) == NULL)
			return -1;
		imagewin_updateBuf(win);
		imagewin_updateScr(win);
		return NOERR ;
	}

int	ImageEraseDBtnProc(int kobj)
// initDataZIMGWIN:idImageEraseDBtn:MJ_DBUTTONL40の呼び出し関数
{
	IMWIN *win;
	if ((win = imagewin_getWinFromPart(kobj)) == NULL)
		return -1;
	char buf[250];
	sprintf(buf, "画像 %s のウィンドウをクローズします。"
	        "画像データは破棄されますが、よろしいですか？",
	        (win->fname[0]==0 ? "<名称未定>" : win->fname));
	if (dispCheckMessage("画像ウィンドウを閉じる", buf, "続行") == 0)
	{
		imagewin_unlink(win);
		imagewin_destroy(win);
		if (imagewin_getCurrentWin() == win)
			imagewin_setCurrentWin(NULL);
	}
	return NOERR ;
}


/*--------------------------------------------------------*/
/*                   自由曲線による描画                   */
/*--------------------------------------------------------*/

static int pen_ofsx, pen_ofsy;

static void _putPixel(IMWIN *win, POINT *pt)
/* 画像内の位置 pt に点を置く */
{
	PIXEL pixel;
	colsel_getPixel(&pixel);
	pic_psetpen(win->pic,pt->x,pt->y,pensel_curpen(),&pixel);
}

static void _flushPointBuf(IMWIN *win, FIFO *points, int tooltype)
{
	if (fifo_isEmpty(points))
		return;
	pic_beginUpDate(win->pic);
  /* ４座標ずつまとめて処理 */
	int i;
	for (i=0; i<4; i++)
	{
		if (fifo_isEmpty(points))
			break;
		POINT mosp, imgp;
	  /* マウスカーソル位置が画像内のどこであるかを得る */
		fifo_get(points, &mosp);
		imagewin_getPosFromMos(win, &mosp, &imgp);
		PIXEL pixel;  colsel_getPixel(&pixel);
		Pen pen = pensel_curpen();
		switch(tooltype)
		{
		case TOOL_NIZIMI:
			pic_blot(win->pic, imgp.x,imgp.y,2,paramNizimi, &pixel);
			break;
		case TOOL_KOSURI:
			pic_kosuriDrag(win->pic,imgp.x-pen_ofsx,imgp.y-pen_ofsy,
							paramKosuri);
			break;
		case TOOL_BOKASI:
			pic_diffusePen(win->pic,imgp.x,imgp.y,pen);
			break;
		case TOOL_SAND:
			pic_sandPen(win->pic,imgp.x,imgp.y,pen);
			break;
		default:
			_putPixel(win, &imgp);
			break;
		}
	}
  /* 画面(とユーザー領域バッファ)に画像の更新部分を転送 */
	FRAME updatefr;
	pic_endUpDate(win->pic, &updatefr);
	imagewin_updateScrPart(win, &updatefr);
}

static	int draw_freeline(IMWIN *win, EVENT* first_ev, int type)
/* first:最初のマウス座標 */
{
	imagewin_storeUndo(win);
	EVENT *ev = first_ev;
	POINT cp = *(POINT*)&first_ev->info;
	POINT lastp = cp;
  // FIFOバッファの作成
	FIFO *points = fifo_new(sizeof(POINT));
	if (points == NULL)
	{
		dispAlertMessage("メモリ不足です",
			"描画処理を行うためのメモリ領域が不足しています。"
			"不要な画像ウィンドウをクローズしてみてください");
		return NOERR;
	}
  // 最初の点に関する処理
	switch(type)
	{
	case TOOL_KOSURI: {
	    char *graypat;  int wid,ht;
		pensel_getPattern(&graypat, &wid,&ht,&pen_ofsx,&pen_ofsy);
		POINT pt;  imagewin_getPosFromMos(win, &cp, &pt);
		pic_kosuriStart(win->pic,pt.x-pen_ofsx,pt.y-pen_ofsy,
			graypat,wid,ht);
		} break;
	case TOOL_PAINT: {
		POINT pt;  imagewin_getPosFromMos(win, &cp, &pt);
		PIXEL pix; colsel_getPixel(&pix);
		int csr;
		MG_PushPtr(MOSICON_WAIT, &csr);
		pic_beginUpDate(win->pic);
		pic_paint(win->pic,pt.x,pt.y, &pix);
		FRAME frUpdate;
		pic_endUpDate(win->pic, &frUpdate);
		MG_PopPtr(csr);
		imagewin_updateScrPart(win, &frUpdate);
		} break;
	}
  /* イベントループ */
	BOOL fFirst = TRUE;
	int ev_ret = NOERR;
	for (;;)
	{
	  // マウスボタンが放されたら終了
		if (!fFirst && ev->what!=EVMOSDRAG && (ev->shift&SLEFTBTN)!=0)
			break;
	  // PIC領域にカーソルがあれば、座標登録
		else if (imagewin_PtInPic(win, &cp))
		{
		  /* FIFOバッファへ座標を登録する関数 */
			void push_p(int x,int y)
			{
				POINT p;  p.x = x;  p.y = y;
				fifo_set(points,&p);
			}
		  /* 処理種別ごとに分岐 */
			switch (type)
			{
			case TOOL_KOSURI:
			case TOOL_NIZIMI:
				push_p(cp.x, cp.y);
				break;
			case TOOL_FPSET:
			case TOOL_BOKASI:
			case TOOL_SAND:
				if (ev_ret == NOERR)
					push_p(cp.x, cp.y);
				break;
			case TOOL_FLINE:
				if (ev_ret == NOERR)
					do_line_cont(lastp.x,lastp.y,cp.x,cp.y, push_p);
				break;
			}
			lastp = cp;
		}
		if (!fifo_isEmpty(points))
			_flushPointBuf(win,points,type);
	  // イベントセンス
		MMI_iosense();
		EVENT *pre_ev = ev;
		if ((ev_ret = MMI_GetEvnt(EVMOSDRAG|EVMOSUP,&ev)) != NOERR)
			ev = pre_ev;
		else
			fFirst = FALSE;
		cp = *(POINT*)&ev->info;
	}
  /* 座標バッファのフラッシュ */
	while (!fifo_isEmpty(points))
		_flushPointBuf(win,points,type);
	fifo_destroy(points);
  /* 次に続くイベントループ処理のために、最後のイベントをキューに戻す */
	EVENT evbuf;
	evbuf = *ev;
	MMI_SetEvnt(&evbuf);
	return NOERR ;
}

/*--------------------------------------------------------*/
/*                 直線、矩形、矩形フィル                 */
/*--------------------------------------------------------*/

static	int		draw_line(IMWIN *win, POINT *first, int type)
/* first:最初のマウス座標 */
/* type:0=直線  1=矩形  2=矩形フィル */
{
	imagewin_storeUndo(win);
	int ret;
	EVENT *ev, evbuf;
	WINCLIP *pstackClip ;
	FIFO *points;
	POINT lastp;
	int rub_x, rub_y;
  /* FIFOバッファの作成 */
	points = fifo_new(sizeof(POINT));
	lastp = *first;
	rub_x = lastp.x, rub_y = lastp.y;
  /* クリップ枠の設定 */
	RM_setOriginZero();
	RM_setClipWinUser(win->win, &pstackClip) ; 
  /* イベントループ */
	for (;;)
	{
		int what,shift;
		POINT *pt;
	  /* ラバー描画 */
		MG_mosDisp(2);
		if (type == 0)
			{ WGB_LINE(guiEgbPtr,lastp.x,lastp.y,rub_x,rub_y,
			           MG_colorChange(WHITE), 4); }
		else
			{ WGB_BOXLINE(guiEgbPtr,lastp.x,lastp.y,rub_x,rub_y,
			              MG_colorChange(WHITE), 4); }
		MG_mosDisp(3);
	  /* イベントセンス */
		do {
			MMI_iosense();
		} while (MMI_GetEvnt(EVALL, &ev) != NOERR);
	  /* ラバー消去 */
		MG_mosDisp(2);
		if (type == 0)
			{ WGB_LINE(guiEgbPtr,lastp.x,lastp.y, rub_x, rub_y,
			           MG_colorChange(WHITE), 4); }
		else
			{ WGB_BOXLINE(guiEgbPtr,lastp.x,lastp.y, rub_x, rub_y,
			              MG_colorChange(WHITE), 4); }
		MG_mosDisp(3);
	  /* イベント種別、シフト状態、マウス座標を得る */
		what  = ev->what;
		shift = ev->shift;
		pt    = (POINT *) &ev->info;
	  /* 次のラバー座標を得る */
		rub_x = pt->x;
		rub_y = pt->y;
	  /* 左ボタンがクリックされたら */
		if (what == EVMOSDN && (shift & SLEFTBTN) != 0)
		{
			FRAME fr; RM_getWinUserFrame(win->win, &fr);
			if (fr.X <= pt->x && pt->x <= fr.X2 &&
			    fr.Y <= pt->y && pt->y <= fr.Y2)
			{
				void pset(int x,int y)
				{
					POINT p;  p.x = x;  p.y = y;
					fifo_set(points,&p);
				}
				switch (type)
				{
				case 0:
					do_line(lastp.x,lastp.y,pt->x,pt->y, pset);
					while (!fifo_isEmpty(points))
						_flushPointBuf(win, points, TOOL_FPSET);
					break;
				case 1:
					do_boxline(lastp.x,lastp.y,pt->x,pt->y, pset);
					while (!fifo_isEmpty(points))
						_flushPointBuf(win, points, TOOL_FPSET);
					break;
				case 2:
					POINT mosp,imgp1,imgp2;
					FRAME updatefr,scrfr;
					PIXEL pixel;
					int min_y,max_y,i;
					colsel_getPixel(&pixel);
					mosp.x = lastp.x, mosp.y = lastp.y;
					imagewin_getPosFromMos(win, &mosp, &imgp1);
					mosp.x = pt->x,   mosp.y = pt->y;
					imagewin_getPosFromMos(win, &mosp, &imgp2);
					min_y = _min(imgp1.y, imgp2.y);
					max_y = _max(imgp1.y, imgp2.y);
				  /* 画面更新の開始 */
					MG_mosDisp(2);
					pic_beginUpDate(win->pic);
				  /* 矩形の描画 */
					for (i=min_y; i<=max_y; i++)
						pic_grayhline(win->pic,imgp1.x,imgp2.x,i,
									  pensel_getmix(),&pixel);
				  /* 画面に画像の更新部分を転送 */
					pic_endUpDate(win->pic, &updatefr);
					imagewin_updateScrPart(win, &updatefr);
				  /* 画面更新の終了 */
					MG_mosDisp(3);
				}
				lastp = *pt;
				if (type != 0)
					break;
			}
		}
	  /* 右ボタンがクリックされたら */
		else if (what == EVMOSDN && (shift & SRIGHTBTN) != 0)
		{
			break;
		}
	}
  /* クリップ枠の復帰 */
	RM_resetClipWinUser(pstackClip) ;
	RM_recoverOrigin();
  /* FIFOバッファの消滅 */
	fifo_destroy(points);
  /* 次に続くイベントループ処理のために、マウス左ボタンの EVMOSUP イベントを
     キューに積む */
	evbuf = *ev;
	evbuf.what = EVMOSUP;
	evbuf.shift = (evbuf.shift & (~SRIGHTBTN)) | SLEFTBTN;
	MMI_SetEvnt(&evbuf);
	return NOERR ;
}
