/*********************************************************************
 *
 *            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 _TOOL_BOX_C_
#include "ezfm.h"

/********************************************************************************/
void  addItemInToolBox       MY_ANSIARGS((char *page, char *clr, char *name, char *spec));
int ezfmEncodeToolInfo MY_ANSIARGS((void *obj, void *data, char **message_ret,
				    int *length_ret, int *needFree));
int ezfmDecodeToolInfo MY_ANSIARGS((void *obj, void *data, char *message, int length));
int toolExecByName      MY_ANSIARGS((void *item, void *data, char *message, int length));
int toolExecByContent   MY_ANSIARGS((void *item, void *data, char *message, int length));
void toolItemOrIconCB   MY_ANSIARGS((EZ_Item *item, void *data));
char *getToolSpec       MY_ANSIARGS((char *pathname));
/********************************************************************************/

static void toolItemDestroyCB  MY_ANSIARGS((EZ_Item *item, void *data));
static int  toolItemDropOnRootWin MY_ANSIARGS((EZ_Item *item, void *data, char **message,
					       int *length, int *needFree));
static int  substituteCommand MY_ANSIARGS((char *cmd, char *cmd_ret));
static void tabCb MY_ANSIARGS((EZ_Widget *tab, void *data));
/********************************************************************************/
typedef struct {
  char      *name;
  EZ_Widget *page;
} oneNBPage;
/********************************************************************************/
static void closeToolBox(w,d) EZ_Widget *w; void *d;
{ EZ_HideWidget( (EZ_Widget *)d);}

void addItemInToolBox(pg, clr, label, tspec) char *pg, *clr; char *label; char *tspec;
{
  EZ_Widget *workarea, *notebook, *thePage;
  oneNBPage *pages;
  int       i = 0;
  
  if(ezfmToolBox == NULL)
    {
      char titleStr[256];
      EZ_Widget *mbar, *menu;
      EZ_LabelPixmap *xpm = EZ_CreateLabelPixmapFromXpmFile("tools.xpm");

      sprintf(titleStr, "%s@%s Tools", EZFMVERSIONSTRING, ezfmHostname);
      ezfmToolBox =   EZ_CreateWidgetXrm(EZ_WIDGET_FRAME,   NULL,
					 "ToolBox",         "toolBox",
					 EZ_FILL_MODE,      EZ_FILL_BOTH,
					 EZ_ORIENTATION,     EZ_VERTICAL,
					 EZ_SIZE,            640, 300,
					 EZ_PADX,               2,
					 EZ_PADY,               0,
                                         EZ_BG_PIXMAP,      bigPixmap,
                                         EZ_EVENT_HANDLER,   ConfigureNotifyHandler, NULL,
					 EZ_WM_WINDOW_NAME, titleStr,
					 NULL);
      if(xpm) EZ_ConfigureWidget(ezfmToolBox, EZ_WM_ICON_PIXMAP, xpm, 0);

      mbar =           EZ_CreateWidget(EZ_WIDGET_MENU_BAR,     ezfmToolBox,
				       EZ_HEIGHT,              0,
				       EZ_IPADX,               6,
				       EZ_PADX,                0,
				       EZ_PADY,                0,
				       NULL);
      
      menu = EZ_CreateSimpleMenu("Close %f", closeToolBox, ezfmToolBox);
      (void) EZ_MenuBarAddItemAndMenu(mbar, "File", 0, menu);
      
      notebook = EZ_CreateWidget(EZ_WIDGET_NOTE_BOOK,      ezfmToolBox, 
				 0);
      pages = (oneNBPage *)EZ_Malloc(30 * sizeof(oneNBPage));
      memset(pages, 0, 30 * sizeof(oneNBPage));
      EZ_SetWidgetPtrData(notebook, pages);
      EZ_SetWidgetPtrData(ezfmToolBox, notebook);
    }
  notebook = (EZ_Widget *)EZ_GetWidgetPtrData(ezfmToolBox);
  pages = (oneNBPage *)EZ_GetWidgetPtrData(notebook);
  if(pg == NULL) pg = "Main";
  /* look for notebook page labeled by 'pg' */
  for(i = 0; i < 30; i++)
    if(pages[i].name == NULL || !strcmp(pages[i].name, pg)) break;

  /* create a new page if needed */
  if(pages[i].name == NULL)
    {
      EZ_Widget *tmp, *tab;
      int len = strlen(pg);
      EZ_Widget *nbpage = EZ_CreateWidget(EZ_WIDGET_NOTEBOOK_PAGE,  notebook,
					  EZ_NAME,                  pg,
					  EZ_LABEL_STRING,          pg,
					  EZ_FILL_MODE,             EZ_FILL_BOTH,
					  EZ_BACKGROUND, clr == NULL? "black" : clr,
					  EZ_PADX,                  3,
					  EZ_PADY,                  3,
					     NULL);
      pages[i].name = (char *)EZ_Malloc( (len+1) * sizeof(char));
      strcpy(pages[i].name, pg);

      tab = EZ_GetNoteBookPageTab(nbpage);
      EZ_ConfigureWidget(tab, 
			 EZ_CALLBACK,   tabCb, (void *)i,
			 EZ_FONT_ID,    ezfmFontId[LABEL_MEDIUM_FONT],
			 NULL);
      
      pages[i].page = nbpage;
      workarea =   EZ_CreateWidget(EZ_WIDGET_WORK_AREA,  nbpage,
				   EZ_IPADX, 20, 
				   EZ_IPADY, 20,
				   EZ_BORDER_WIDTH, 1,
				   EZ_ITEM_MOVABLE,       False, 
				   NULL);
      EZ_SetWidgetPtrData(nbpage, workarea);
  
      tmp = EZ_CreateWidget(EZ_WIDGET_NORMAL_BUTTON, workarea,
			    EZ_GRID_CELL_GEOMETRY,   1, 1, 1, 1, /* x, y, w, h */
			    EZ_GRID_CELL_PLACEMENT,  EZ_FILL_BOTH, EZ_CENTER,
			    EZ_BORDER_WIDTH,         0,
			    EZ_BORDER_TYPE,          EZ_BORDER_GROOVE,
			    EZ_BITMAP_FILE,          "rball_s.xpm",
			    EZ_PADX, 0, EZ_PADY,0, EZ_IPADX, 0, EZ_IPADY,0,
			    EZ_CALLBACK,             setBackdropCB, workarea,
			    NULL);
    }

  thePage = pages[i].page;
  workarea = (EZ_Widget *) EZ_GetWidgetPtrData(thePage);
  {
    EZ_Item      *item;
    char         lxpmFile[64], sxpmFile[64], *pixmap_file;
    char         *clr = "black", color[16];
    char         action[256];
    char         pathname[1024], *ptr;
    int          len, fontId;
    FileInfo     *finfo;
    EZ_LabelPixmap *labPixmap;

    if(locateCommand(label, pathname))
      {
	finfo = (FileInfo *)EZ_Malloc(sizeof(FileInfo));
	memset(finfo, 0, sizeof(FileInfo));
	len = strlen(pathname) + 1;
	ptr = (char *)EZ_Malloc(len * sizeof(char));
	strcpy(ptr, pathname);
	finfo->pathname = ptr;
	len = strlen(tspec) + 1;
	ptr = (char *)EZ_Malloc(len * sizeof(char));
	strcpy(ptr, tspec);
	finfo->ezfmData = ptr;
	getSpecPixmap(tspec, lxpmFile, sxpmFile);
	getSpecColor(tspec, color); if(color[0]) clr = color;
	fontId = getSpecFontId(tspec);
    
	labPixmap = EZ_CreateLabelPixmapFromXpmFile(lxpmFile);
	if(!labPixmap) labPixmap = EZ_CreateLabelPixmapFromXpmFile("unknown.xpm");
	item = EZ_CreateItem(EZ_LABEL_ITEM, 
			     EZ_PADX,              6,
			     EZ_PADY,              4,
			     EZ_ORIENTATION,       EZ_VERTICAL,
			     EZ_LABEL_STRING,      label,
			     EZ_FOREGROUND,        clr,
			     EZ_LABEL_PIXMAP,      labPixmap,
			     EZ_FONT_ID,           ezfmFontId[fontId],
			     EZ_TEXT_LINE_LENGTH,  32,
			     EZ_CLIENT_PTR_DATA,   finfo,
			     EZ_CALLBACK,          toolItemOrIconCB, finfo,
			     EZ_DESTROY_CALLBACK,  toolItemDestroyCB, finfo,
			     NULL);

	EZ_ItemAddDnDDataEncoder(item, EZ_DND_DRAG_INIT_ATOM,  /* drag initialization  */
				 0, ezfmSetDragIconEncoder, finfo, NULL, NULL);
	/* try to make item behave the same as other file items when draged */
	/* What to do if item is droped to the root window */
	EZ_ItemAddDnDDataEncoder(item, ezfmFileNameAtom, (~0), 
				 toolItemDropOnRootWin, finfo, NULL, NULL);
	EZ_ItemAddDnDDataEncoder(item, EZ_DND_DRAG_START_ATOM,  /* drag setup    */
				 0, ezfmIIEncodeStartFilter, finfo, NULL, NULL);
	EZ_ItemAddDnDDataEncoder(item, ezfmFileNameAtom, 0,     /* convert filename */
				 ezfmEncodeFileName, finfo, NULL, NULL);
	EZ_ItemAddDnDDataEncoder(item, ezfmFileInfoAtom, 0,     /* convert fileInfo */
				 ezfmEncodeFileInfo, finfo, NULL, NULL);

	/* DnD action: what to do if item can see the drag in its host's local file system */
	getSpecField(ptr, SPEC_DROP_BY_NAME_ACTION, action);
	if(action[0] != '\0')
	  EZ_ItemAddDnDDataDecoder(item, ezfmFileNameAtom, 0,
				   toolExecByName, finfo, NULL, NULL);
	/* DnD action: what to do if item can't see the drag in its host's local file system */
	getSpecField(ptr, SPEC_DROP_BY_CONTENT_ACTION, action);
	if(action[0] != '\0')
	  EZ_ItemAddDnDDataDecoder(item, ezfmFileContentAtom, 0,
				   toolExecByContent, finfo, NULL, NULL);

	/* DnD action: what to do if item is draged and droped on an item/icon */

	EZ_ItemAddDnDDataEncoder(item, ezfmToolExecutionAtom, 0,
				 ezfmEncodeToolInfo, finfo, NULL, NULL);

	EZ_WorkAreaInsertItem(workarea, item);
      }
  }
}
/********************************************************************************/
static void tabCb(widget, data) EZ_Widget *widget; void *data;
{
  if(EZ_WidgetExist(ezfmToolBox))
    EZ_SetWidgetIntData(ezfmToolBox, (int)data);
}
/********************************************************************************/

static void toolItemDestroyCB(item, data) EZ_Item *item; void *data;
{
  FileInfo *finfo = (FileInfo *)data;
  if(finfo)
    {
      EZ_Free(finfo->pathname);
      /* EZ_Free(finfo->ezfmData);*/
      EZ_Free(finfo);
    }
}
/********************************************************************************
 *
 *  What to do if one double clicks on a tool item
 */
void toolItemOrIconCB(item, data) EZ_Item *item; void *data; /* double click action */
{
  char       tcmd[512], command[512];
  FileInfo   *finfo = (FileInfo *)data;
  char *tspec = finfo->ezfmData;
  getSpecField(tspec, SPEC_DCLICK_ACTION, tcmd); 
  
  if(substituteCommand(tcmd, command))
    {
      oneCommandDialogueNCF("Tool Execution", "Please confirm", command, 0,
                            NULL, 0, NULL, 
                            (ezfmFlags & DONT_CONFIRM_TOOL_BIT));
    }
  else 
    {
      char *path = finfo->pathname;
      ezfmExecutionErrorI("It looks like that the command", path,
			  "needs a file when executed. Try", 
			  "drag a file to its icon");
    }
}
/********************************************************************************
 *
 *  what to do if an item is draged and droped on a tool item
 */
int toolExecByName(item, data, message, length)
     void *item; void *data; char *message; int length;
{
  if(length > PATHNAME_OFFSET)
    {
      char taction[256], action[256], command[1024], *spec, *myname;
      char *pathname = message + PATHNAME_OFFSET;
      FileInfo *finfo = (FileInfo *)data;
      spec = finfo->ezfmData;
      myname = finfo->pathname;
      if(strcmp(myname, pathname))
	{
	  getSpecField(spec, SPEC_DROP_BY_NAME_ACTION, taction);
	  if(taction[0] != '\0')      
	    {
	      if(substituteCommand(taction, action))
		 {
		   ezfmProcessCmdline(action, pathname, command);
		   oneCommandDialogue("Tool Execution", "Please confirm", command, 0,
				      NULL, 0, NULL);
		   return(EZ_DND_SUCCESS);      
		 }
	    }
	}
    }
  return(EZ_DND_FAILURE);
}
/********************************************************************************
 *
 * Things to do if a toolItem is draged and droped on an fileItem/icon.
 *
 * We supply the target with the full-command-pathname and the two
 * commands. Stat info is not included in the message since the tools
 * are supposed to be generic, i.e, they should exist on most system
 * and should have the same name.
 */
int ezfmEncodeToolInfo(obj, data, message_ret, length_ret, needFree)
     void *obj; void *data; char **message_ret; int *length_ret; int *needFree;
{
  FileInfo *finfo = (FileInfo *)data;
  char *pathname = finfo->pathname;
  char action[256];
  getSpecField(finfo->ezfmData, SPEC_DROP_BY_NAME_ACTION, action);
  if(action[0] && pathname && strlen(pathname)<170 && strlen(action)<PATHNAME_OFFSET)
    {
      int len = 257;
      char *msg = (char *)EZ_Malloc( len  * sizeof(char));
      strcpy(msg, action);
      msg[PATHNAME_OFFSET-1] = '\0';
      strcpy(msg+PATHNAME_OFFSET, pathname);
      *length_ret = 256;
      *message_ret = msg;
      *needFree = 1;
      return(EZ_DND_SUCCESS);
    }
  return(EZ_DND_FAILURE);        
}
/********************************************************************************/
int ezfmDecodeToolInfo(obj, data, message, length)
     void *obj; void *data; char *message; int length;
{
  char *cmdpath = message + PATHNAME_OFFSET;
  char *action = message;
  char *cmd, command[1024];
  FileInfo *finfo = (FileInfo *)data;
  char *pathname = finfo->pathname;

  cmd = strrchr(cmdpath, '/');
  if(!cmd) return(EZ_DND_FAILURE);
  ezfmProcessCmdline(action, pathname, command);
  oneCommandDialogue("Tool Execution", "Please confirm", command, 0,
		     NULL, 0, NULL);
  return(EZ_DND_SUCCESS);        
}
/********************************************************************************/
static int toolItemDropOnRootWin(item, data, message_ret, length_ret, needFree)
     EZ_Item *item;  void *data; char **message_ret; int *length_ret; int *needFree;
{
  FileInfo  *finfo = (FileInfo *)data;

  if(finfo)
    {
      EZ_Widget *icon;
      int       x, y, rx, ry, w, h;

      EZ_GetDnDDropLocation( &x,&y, &rx, &ry);
      EZ_GetItemDimension(item, &w, &h);
      icon = createToolItemIcon(finfo->pathname, finfo->ezfmData, rx-w/2,ry-h/2);
      EZ_DisplayWidget(icon);
      return(EZ_DND_SUCCESS);
    }
  return(EZ_DND_FAILURE);
}
/********************************************************************************/
char *getToolSpec(pathname) char *pathname;
{
  if(pathname && ezfmToolBox)
    {
      EZ_Widget *noteBook = (EZ_Widget *)EZ_GetWidgetPtrData(ezfmToolBox);  
      oneNBPage *pages = (oneNBPage *) EZ_GetWidgetPtrData(noteBook);  
      int i, j, nitems;
      EZ_Item *item, **items;
      EZ_Widget *workarea;
      for(j = 0; j < 30; j++)
        {
          FileInfo  *finfo;
          if(pages[j].name == NULL) break;
          workarea = (EZ_Widget *)EZ_GetWidgetPtrData(pages[j].page);
          nitems = EZ_GetWorkAreaNumberOfItems(workarea);
          items = EZ_GetWorkAreaData(workarea);
          for(i = 0; i < nitems; i++)
            {
              item = items[i];
              finfo = (FileInfo *)EZ_GetItemPtrData(item);
              if(!strcmp(finfo->pathname, pathname))
                return(finfo->ezfmData);
            }
        }
    }
  return(NULL);
}

/********************************************************************************
 *
 *  what to do if an item is draged and droped on a tool item. 
 *  The tool item cannot see the src in its host file system.
 */
int toolExecByContent(item, data, message, length)
     void *item; void *data; char *message; int length;
{
  return(EZ_DND_FAILURE);
}

/********************************************************************************
 *
 * if cmdIn is $something, do the substitution.
 */
int substituteCommand(cmdIn, cmdOut) char *cmdIn, *cmdOut;
{
  if(cmdIn[0] == '\0') return(0);
  else
    {
      char tmpstr[256];
      char *cmd = cmdIn;
      while(*cmd && (*cmd == ' ' || *cmd == '\t')) cmd++;
      if(*cmd == '$')
	{
	  char *t, *d = tmpstr, *s = cmd + 1;
	  while(*s && *s != ' ' && *s !='\t') *d++ = *s++;  *d = '\0';
	  t = lookupNameFromHashTable(tmpstr);
	  if(t)
	    {
	      strcpy(cmdOut, t);
	      d = tmpstr;
	      while(*s) { *d++ = *s++;} 
	      *d = '\0';
	      strcat(cmdOut, tmpstr);
	    }
	  else return(0);
	}
      else strcpy(cmdOut, cmd);
      return(1);
    }
}
/********************************************************************************/
EZ_Widget *getToolBoxPage(idx) int idx;
{
  if(idx >= 0 && idx < 30)
    {
      EZ_Widget *notebook = (EZ_Widget *)EZ_GetWidgetPtrData(ezfmToolBox);
      oneNBPage *pages = (oneNBPage *)EZ_GetWidgetPtrData(notebook);
      return(pages[idx].page);
    }
  return(NULL);
}
/********************************************************************************/
#undef _TOOL_BOX_C_
