/*********************************************************************
 *
 *            EZFM---EZWGL based file manager for X11
 *
 *             Copyright (C) 1998  Maorong Zou
 *
 *
 *  This file is part of EZFM.
 *
 *  EZFM 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, or (at your option)
 * any later version.
 *
 * EZFM 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 EZFM; see the file COPYING.  If not, write to
 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
 *
 *************************************************************************/




#define _PRINTER_C_
#include "ezfm.h"

/**********************************************************************************/
void setupPrinter  MY_ANSIARGS((void *btn, void *data));
void doPrintDialogue  MY_ANSIARGS((char *pathname));
void printerDialogue MY_ANSIARGS((EZ_Widget *widget, void *data));
/**********************************************************************************/
static int targetStartFilter MY_ANSIARGS((void *obj, void *data, char *msg, int len));
static int decodeFileName    MY_ANSIARGS((void *obj, void *data, char *message, int length));
static int decodeFileContent MY_ANSIARGS((void *obj, void *data, char *message, int length));

static void printerBtnCallback MY_ANSIARGS((EZ_Widget *widget, void *data));
static void printerEHandler MY_ANSIARGS ((EZ_Widget* widget,void *data, int etype, XEvent *xev));

/**********************************************************************************
 *
 * setup printer btn/item
 */
void setupPrinter(obj, data)
     void *obj; void *data;
{
  EZ_Widget *pbtn = (EZ_Widget *)obj;
  EZ_Widget *frame = (EZ_Widget *)data; /* parent frame */
  Toplevel  *toplevel;
  if(frame && (toplevel = (Toplevel *)EZ_GetWidgetPtrData(frame)) != NULL)
    EZ_AddWidgetCallBack(pbtn, EZ_CALLBACK, printerBtnCallback, toplevel, 0);
      
  EZ_AddEventHandler(pbtn, printerEHandler, NULL, 0);
  EZ_WidgetAddDnDDataDecoder(pbtn, EZ_DND_DROP_START_ATOM, /* drop setup    */
			     0, targetStartFilter, NULL, NULL, NULL);
  EZ_WidgetAddDnDDataDecoder(pbtn, ezfmFileNameAtom, 0,
			     decodeFileName, NULL, NULL, NULL);
  EZ_WidgetAddDnDDataDecoder(pbtn, ezfmFileContentAtom, 0,
			     decodeFileContent, NULL, NULL, NULL);
}
/***************************************************************************
 *
 * Decode start filter. If drag is a local file and can be read, use FileName,
 * otherwise, try FileContent.
 */
static int targetStartFilter(obj, data, msg, len)
     void *obj; void *data; char *msg; int len;
{
  Atom          *atomsInOut, theAtom;
  int           *natomsInOut;
  unsigned int  *atypeOut;
  int           i, j, answer;
  char          *src = msg + PATHNAME_OFFSET;

  EZ_DnDGetMatchedConversionTargets(&atomsInOut,&natomsInOut, &atypeOut);  

  if(canSeeTheFile(src, msg) && access(src, R_OK) == 0) theAtom = ezfmFileNameAtom;
  else theAtom = ezfmFileContentAtom;
  for(j = 0, i = 0; i < *natomsInOut; i++)
    {
      Atom atom = atomsInOut[i]; 
      if(atom == theAtom) { atomsInOut[j++] = atom; break; }
    }
  *natomsInOut = j;
  return(0);
}
/***************************************************************************
 *
 *   print a local file.
 */
static int decodeFileName(obj, data, message, length)
     void *obj; void *data; char *message; int length;
{
  if(length > PATHNAME_OFFSET)
    {
      char *pathname = message + PATHNAME_OFFSET;
      doPrintDialogue(pathname);
      return(EZ_DND_SUCCESS);      
    }
  return(EZ_DND_FAILURE);
}

/***************************************************************************
 *
 * for non local files, copy the file to /tmp and print it.
 */
static int decodeFileContent(obj, data, message, length)
     void *obj; void *data; char *message; int length;
{
  char *filePath = message + PATHNAME_OFFSET;
  char tmpfile[256];
  int  fd;

  if(getTmpFileName(tmpfile) != NULL) 
    {
      if((fd = open(tmpfile,O_RDWR|O_CREAT, S_IRUSR|S_IWUSR|S_IRGRP)) >= 0)
	{
	  char   *buf = message+ HEADER_OFFSET;
	  size_t size = (size_t)(length - HEADER_OFFSET);
	  int    ret = EZ_DND_SUCCESS;
	  int    when = 0;
	  if(write(fd, buf,  size) != size) ret = EZ_DND_FAILURE;
	  close(fd);
	  if(ret == EZ_DND_SUCCESS) { doPrintDialogue(tmpfile); when= 1800; }
	  deleteTmpFile(tmpfile, when);
	  return(ret);
	}
    }
  return(EZ_DND_FAILURE);      
}
/*********************************************************************************
 *
 *    info button dialogue.
 */
static void printerBtnCallback(widget, data)
     EZ_Widget *widget; void *data;
{
  if(data)
    {
      Toplevel  *tp = (Toplevel *)data;
      Folder    **flds = tp->folder;
      EZ_Item   *item = NULL;

      item = getFolderSelection(flds[0]);
      if(item == NULL) item = getFolderSelection(flds[1]);

      /* print file */
      if(item != NULL)
	{
	  FileInfo *p = (FileInfo *)EZ_GetItemPtrData(item);
	  if(p)
	    {
	      char pathname[512];
              ezfmQuotePathname(p->pathname, pathname);              
	      doPrintDialogue(pathname);
	    }
	}
      else  ezfmWarning("Printing Error", "You have to select a file first");
    }
}

/*************************************************************************
 *
 *  Control only one printer!
 */
typedef struct printerControl_
{
  char lprCommand[80];           /* last lpr command issued */
  char printerName[16];          /* current printer's name  */
  char userName[8];              /* user name               */
  EZ_Widget *printerDialogue;    /* toplevel frame          */
  EZ_Widget *flistbox;
  int DialogueDisplayed;         /* dialogue status         */
  EZ_Timer *lpq_timer;           
} printerControl;
/*************************************************************************/
static printerControl pntrCtrl;
/**************************************************************************/
static void extractPrinterName(cmd, name) char *cmd, *name;
{
  if(name) { strncpy(pntrCtrl.printerName, name, 15);}
  else if(cmd)
    {
      char tmp[256], *p, *q = NULL;
      strcpy(tmp, pntrCtrl.lprCommand);
      p = tmp; tmp[255]= '\0';
      
      while(*p)
	{
	  while(*p && *p != '-') p++;
	  if(*p && p[-1] == ' ' && p[1] == 'P')
	    {
	      p++;
	      q = p+1;
	      while( *p && *p == ' ') p++;
	      while( *p && *p != ' ') p++;
	      if(p == q) q = NULL;
	      break;
	    }
	  else { q = NULL; p++;}
	}
      if(q)
	{
	  char *t = pntrCtrl.printerName;
	  while(q != p) *t++ = *q++; *t = '\0';
	}
      else pntrCtrl.printerName[0] = '\0';
    }
}
/**************************************************************************/
static void executeCallback(widget, data)
     EZ_Widget *widget; void *data; 
{
  EZ_Widget *toplevel = (EZ_Widget *)data;
  EZ_Widget *entry = (EZ_Widget *)EZ_GetWidgetPtrData(toplevel);
  if(entry)
    {
      char *command = EZ_GetEntryString(entry);
      if(command)
	{
	  char *p, *q, *t, *start;
	  p = command; while(*p == ' ') p++;
	  start = p;
	  while(1)
	    {
	      q = p; while(*q && *q != ' ') q++;
	      p = q;  while(*p == ' ') p++;
	      if(*p != '-') { p = q; break;}
	    }
	  q = start;
	  if((int)(p-q) < 80) /* remember printing command */
	    {
	      t = pntrCtrl.lprCommand;
	      while(q != p) *t++ = *q++; *t++ = '\0';
	      extractPrinterName(pntrCtrl.lprCommand,NULL);
	    }
	  executeShCmd(command, NULL, 0, NULL);
	}
    }
  EZ_ReleaseGrab();
  EZ_DestroyWidget(toplevel);
}
/**************************************************************************/
static void cancelCallback(widget, data)
     EZ_Widget *widget; void *data; 
{
  EZ_Widget *toplevel = (EZ_Widget *)data;
  EZ_ReleaseGrab();
  EZ_DestroyWidget(toplevel);
}

void doPrintDialogue(pathname)
     char *pathname;
{
  EZ_Widget *printToplevel, *label, *entry, *frame, *confirm, *cancel;
  char command[256];

  /* set lpr command line */
  if(pntrCtrl.lprCommand[0]) sprintf(command, "%s  %s", pntrCtrl.lprCommand, pathname);
  else
    {
      char *printer = pntrCtrl.printerName;
      if(printer[0] == '\0') printer = getenv("PRINTER");
      if(printer) sprintf(command, "lpr -P%s  %s", printer, pathname);
      else sprintf(command, "lpr %s", pathname);
    }  
  
  /* create  dialogue */
  printToplevel = EZ_CreateWidgetXrm(EZ_WIDGET_FRAME,    NULL,
				     "DoPrintDialogue",  "doPrintDialogue",
				     EZ_LABEL_STRING,    "Print",
				     EZ_FONT_ID,         ezfmFontId[LABEL_LARGE_FONT],
				     EZ_PADY,            8,
				     EZ_IPADY,           10,
				     EZ_TRANSIENT,       True,
				     EZ_ORIENTATION,     EZ_VERTICAL,
				     EZ_FILL_MODE,       EZ_FILL_BOTH,
				     EZ_BG_PIXMAP,          bigPixmap, 
				     EZ_EVENT_HANDLER,      ConfigureNotifyHandler, NULL,
				     NULL);

  label =           EZ_CreateWidget(EZ_WIDGET_LABEL,       printToplevel,
				    EZ_LABEL_STRING,       "Please confirm",
                                    EZ_FOREGROUND,         "red",
                                    EZ_FONT_ID,            ezfmFontId[LABEL_LARGE_FONT], 
                                    EZ_LABEL_SHADOW,       1,1,
				    EZ_TEXT_LINE_LENGTH,   100,
				    NULL);

  entry =           EZ_CreateWidget(EZ_WIDGET_ENTRY,       printToplevel,
				    EZ_TEXT_LINE_LENGTH,   40,
				    EZ_ENTRY_STRING,       command,
				    EZ_SELECTION_BACKGROUND,SelectionBackground,
				    EZ_FONT_ID,            ezfmFontId[ITEM_MEDIUM_FONT],
				    EZ_CALLBACK,           executeCallback, printToplevel,
				    NULL);
  EZ_EntryEditBeginningOfInput(entry);

  
  frame =           EZ_CreateWidget(EZ_WIDGET_FRAME,       printToplevel,
				    NULL);
  confirm =         EZ_CreateWidget(EZ_WIDGET_NORMAL_BUTTON,   frame,
				    EZ_LABEL_STRING,           "Confirm",
				    EZ_CALLBACK,               executeCallback, printToplevel,
				    NULL);

  cancel =          EZ_CreateWidget(EZ_WIDGET_NORMAL_BUTTON,   frame,
				    EZ_LABEL_STRING,           "Cancel",
				    EZ_CALLBACK,               cancelCallback, printToplevel,
				    NULL);

  EZ_SetWidgetPtrData(printToplevel, entry);
  EZ_DisplayWidgetUnderPointer(confirm, -10, -10);
  EZ_SetGrab(printToplevel);
  EZ_SetFocusTo(confirm);
}
/**************************************************************************************/
static void printerEHandler(widget, data, etype, xev)
     EZ_Widget *widget; void *data; int etype; XEvent *xev;
{
  if(xev->type == ButtonPress && xev->xbutton.button == EZ_Btn2)
    {
      EZ_Widget *menu = (EZ_Widget *)EZ_GetWidgetPtrData(widget);
      if(menu == NULL)
	{
	  menu = EZ_CreateSimpleMenu("Printer %t|Open %f",  printerDialogue, NULL);
	  EZ_SetWidgetPtrData(widget, menu);
	}
      EZ_DoPopupX(widget, menu, -1);
    }
}
/**************************************************************************************/

static void hideCallback     MY_ANSIARGS((void *widget, void *data));
static void dequeueCallback  MY_ANSIARGS((void *widget, void *data));
static void newPrinter       MY_ANSIARGS((void *widget, void *data));
static void flbMCallback     MY_ANSIARGS((void *widget, void *data));
static void lpq_timer_callback  MY_ANSIARGS((void *widget, void *data));
static void updateFListbox   MY_ANSIARGS((void *flistbox));
/*************************************************************************/
static char           last_selection[256];
/*************************************************************************/

void printerDialogue(widget, data) EZ_Widget *widget; void *data;
{
  if(!EZ_WidgetExist(pntrCtrl.printerDialogue))
    {
      EZ_Widget  *frame, *tframe, *flistbox, *rmbutton, *hidebutton, *tmp;
      EZ_Widget  *printerEntry;

      if(pntrCtrl.userName[0] == 0)
	{
	  struct passwd *pwd = getpwuid(getuid());
	  if(pwd)strcpy(pntrCtrl.userName, pwd->pw_name);
	  else strcpy(pntrCtrl.userName, "unknown");
	}
      if(pntrCtrl.printerName[0] == 0)
	{
	  char *printer = getenv("PRINTER");
	  if(printer) strcpy(pntrCtrl.printerName, printer);
	}
      /* the dialogue popup */
      frame =     EZ_CreateWidgetXrm(EZ_WIDGET_FRAME,    NULL,
				     "PrinterDialogue",  "printerDislogue",
				     EZ_LABEL_STRING,    "Printer Queue",
				     EZ_FONT_ID,         ezfmFontId[LABEL_LARGE_FONT],
				     EZ_ORIENTATION,     EZ_VERTICAL,
				     EZ_HEIGHT,          300, 
				     EZ_IPADY,           8,
				     EZ_TRANSIENT,       True,
				     EZ_FILL_MODE,       EZ_FILL_BOTH,
				     EZ_BG_PIXMAP,          bigPixmap, 
				     EZ_EVENT_HANDLER,      ConfigureNotifyHandler, NULL,
				     0);
      pntrCtrl.printerDialogue = frame;

      /* fancy listbox, display printing queue */
      flistbox =   EZ_CreateWidget(EZ_WIDGET_FANCY_LIST_BOX, frame,
				   EZ_FANCY_LISTBOX_COLUMNS, 1,
				   EZ_FANCY_LISTBOX_TYPE,    1,
				   EZ_BORDER_WIDTH,          1,
				   EZ_SELECTION_FOREGROUND, "blue",			       
				   EZ_SELECTION_BACKGROUND, "yellow",
				   EZ_MOTION_CALLBACK,       flbMCallback,  last_selection, 
				   NULL);
      pntrCtrl.flistbox = flistbox;



      /* a frame, holds an optional entry and two buttons */
      tframe =      EZ_CreateWidget(EZ_WIDGET_FRAME,    frame,
				    EZ_BORDER_WIDTH,    2,
				    EZ_BORDER_TYPE,     EZ_BORDER_ENGRAVED,
				    EZ_ORIENTATION,     EZ_HORIZONTAL_LEFT, 
				    EZ_HEIGHT,          0,
				    EZ_FILL_MODE,       EZ_FILL_VERTICALLY,
				    EZ_GRID_CONSTRAINS, EZ_COLUMN,  1,   100,  100,  0,
				    EZ_GRID_CONSTRAINS, EZ_COLUMN,  2,   100,  100,  0,
				    EZ_GRID_CONSTRAINS, EZ_COLUMN,  4,   10,   50,  0,
				    0);

      /* opt entry for type in printer name */
      tmp =        EZ_CreateWidget(EZ_WIDGET_LABEL,             tframe, 
				   EZ_LABEL_STRING,             "Printer:",
				   EZ_LABEL_POSITION,           EZ_RIGHT,
				   EZ_PADX,                     0,
				   EZ_GRID_CELL_GEOMETRY,       0, 0 , 1, 1,    
				   EZ_GRID_CELL_PLACEMENT,      EZ_FILL_HORIZONTALLY, EZ_RIGHT,
				   NULL);
      printerEntry =EZ_CreateWidget(EZ_WIDGET_OPTIONAL_ENTRY,    tframe, 
				    EZ_ENTRY_STRING,             pntrCtrl.printerName,
				    EZ_OPTIONAL_ENTRY_EDITABLE,  True,
				    EZ_CALLBACK,                 newPrinter, NULL,
				    EZ_OPTIONAL_ENTRY_REMEMBER_HISTORY, 1,
				    EZ_GRID_CELL_GEOMETRY,       1, 0 , 1, 1,	 
				    EZ_GRID_CELL_PLACEMENT,      EZ_FILL_HORIZONTALLY, EZ_LEFT,
				    0);
      rmbutton =    EZ_CreateWidget(EZ_WIDGET_NORMAL_BUTTON,    tframe,
				    EZ_LABEL_STRING,            "Remove Job",
				    EZ_UNDERLINE,                0,
				    EZ_CALLBACK,                 dequeueCallback, flistbox,
				    EZ_GRID_CELL_GEOMETRY,       3, 0 , 1, 1,	 
				    NULL);
      EZ_SetWidgetPtrData(flistbox, rmbutton);
      EZ_DisableWidget(rmbutton);

      hidebutton =  EZ_CreateWidget(EZ_WIDGET_NORMAL_BUTTON,    tframe,
				    EZ_LABEL_STRING,           "Hide",
				    EZ_UNDERLINE,               0,
				    EZ_CALLBACK,                hideCallback, NULL,
				    EZ_GRID_CELL_GEOMETRY,      5, 0 , 1, 1,	 
				    NULL);

      /* check printing queue  every 5 seconds */
      pntrCtrl.lpq_timer = EZ_CreateTimer(5,0,-1,lpq_timer_callback,flistbox,0);
      
      /* fancy listbox accept drops */
      EZ_WidgetAddDnDDataDecoder(flistbox, EZ_DND_DROP_START_ATOM, /* drop setup    */
				 0, targetStartFilter, NULL, NULL, NULL);
      EZ_WidgetAddDnDDataDecoder(flistbox, ezfmFileNameAtom, 0,
				 decodeFileName, NULL, NULL, NULL);
      EZ_WidgetAddDnDDataDecoder(flistbox, ezfmFileContentAtom, 0,
				 decodeFileContent, NULL, NULL, NULL);
    }
  EZ_RestartTimer(pntrCtrl.lpq_timer);
  EZ_DisplayWidget(pntrCtrl.printerDialogue);
  EZ_RaiseWidgetWindow(pntrCtrl.printerDialogue);
  pntrCtrl.DialogueDisplayed = 1;
  updateFListbox(pntrCtrl.flistbox);
}
/***********************************************************************/

static void lpq_timer_callback(timer, data)
     EZ_Timer *timer; void *data;
{
  EZ_Widget *flistbox = (EZ_Widget *)data;
  if(flistbox) updateFListbox(flistbox);
}
/***********************************************************************/
void updateFListbox(flistbox)
     EZ_Widget *flistbox;
{
  if(flistbox && pntrCtrl.DialogueDisplayed != 0)
    {
      EZ_Item *item, *items[64], *sitem = NULL;
      EZ_Widget *rmbtn = (EZ_Widget *)EZ_GetWidgetPtrData(flistbox);
      char str[256], fstr[256];
      int i,j, k;
      FILE *fp, *popen();
      EZ_FreezeWidget(flistbox);
      EZ_FancyListBoxClear(flistbox);
      sprintf(str, "lpq -P%s", pntrCtrl.printerName);
      fp = popen(str, "r");
      i = 0;
      while(fgets(str, 256, fp))
	{
	  if(isdigit(str[0]) || !strncmp(str, "active",6) ||
	     !strncmp(str, "error",3))
	    {
	      item = items[i] = EZ_CreateLabelItem(str, NULL);
	      i++;
	      EZ_ConfigureItem(item, EZ_TEXT_LINE_LENGTH,128,
			       EZ_FONT_NAME, "-*-Courier-Medium-R-Normal--*-140-*-*-*-*-*-*",
			       0);
	      if(!strcmp(last_selection, str))
		sitem = item;
	      {
		char f1[16], f2[16];
		sscanf(str, "%s  %s", f1, f2);
		if(!strncmp(f2,pntrCtrl.userName,strlen(pntrCtrl.userName)))
		  EZ_ConfigureItem(item, EZ_FOREGROUND,"red",0);
	      }
	    }
	}
      pclose(fp);
      EZ_SetFancyListBoxData(flistbox, items, i, 1);
      if(sitem) 
	{
	  EZ_FancyListBoxSelectItem(flistbox, sitem, NULL);
	  EZ_EnableWidget(rmbtn);
	}
      else  EZ_DisableWidget(rmbtn);
      EZ_UnFreezeWidget(flistbox);
    }
}

/***********************************************************************/
void hideCallback(widget, data)    EZ_Widget *widget; void *data;
{
  EZ_HideWidget(pntrCtrl.printerDialogue);
  pntrCtrl.DialogueDisplayed = 0;
}
/***********************************************************************
 *
 *  remove the selected printing job
 */
static void removeJob(flistbox)  EZ_Widget *flistbox;
{
  if(flistbox)
    {
      EZ_Item **items = EZ_GetFancyListBoxSelection(flistbox);
      if(items)
	{
	  EZ_Item *item = items[0];
	  char *str = NULL;
	  int length = 0;
	  EZ_GetLabelItemStringInfo(item, &str, &length);
	  if(str)
	    {
	      int jobNum = 0;
	      char f1[64], f2[64], f3[64];
	      sscanf(str, "%s  %s  %s %d ", f1, f2, f3, &jobNum);
	      printf("%s %s %d\n", f1,f2,jobNum);
	      if(jobNum)
		{
		  char cmd[128];
		  sprintf(cmd, "lprm -P%s %d\n", pntrCtrl.printerName, jobNum);
		  system(cmd); 
		  EZ_CreateTimer(0, 200000, 0, lpq_timer_callback, flistbox,0);
		  EZ_RestartTimer(pntrCtrl.lpq_timer);
		}
	    }
	}
    }
}

/***********************************************************************/
void dequeueCallback(widget, data)  EZ_Widget *widget; void *data;
{
  EZ_Widget *flistbox = (EZ_Widget *)data;
  removeJob(flistbox);
}
/***********************************************************************/
void newPrinter(widget, data)  EZ_Widget *widget; void *data;
{
  char *str = EZ_GetOptionalEntryString(widget);
  if(str) strcpy(pntrCtrl.printerName, str);
  EZ_RestartTimer(pntrCtrl.lpq_timer);
  updateFListbox(pntrCtrl.flistbox);
}
/***********************************************************************/
static void flbMCallback(widget,data) EZ_Widget *widget; void *data;
{
  EZ_Item    **items = EZ_GetFancyListBoxSelection(widget);
  EZ_Widget  *rbtn = (EZ_Widget *)EZ_GetWidgetPtrData(widget);
  if(items)
    {
      EZ_Item    *item = *items;
      if(item)
	{
	  char *str; int len;
	  EZ_GetLabelItemStringInfo(item, &str, &len);
	  if(len > 0) strcpy(last_selection,str);
	  EZ_EnableWidget(rbtn);
	  return;
	}
    }
  EZ_DisableWidget(rbtn);
}
/************************************************************************/
#undef _PRINTER_C_
