/* micon.c 
 * Copyright (C) 2002 Jean-Jacques Sarton jj.sarton@t-online.de
 *
 * 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 2 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, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include <Xm/Xm.h>
#include <Xm/MwmUtil.h>
#include <X11/Shell.h>
#include <Xm/PanedW.h>
#include <Xm/MainW.h>
#include <Xm/Form.h>
#include <Xm/ScrollBar.h>
#include <Xm/ScrolledW.h>
#include <Xm/List.h>
#include <Xm/Separator.h>
#include <Xm/RowColumn.h>
#include <Xm/TextF.h>
#include <Xm/DrawingA.h>

#include <Xm/PushB.h>
#include <Xm/Label.h>
#include <Xm/Frame.h>
#include <Xm/MwmUtil.h>
#ifdef MOTIF_XPM
# include <Xm/XpmP.h>
# define XPM_OK 1
#else
# ifdef XPM_XPM
#  include <X11/xpm.h>
# define XPM_OK 1
# endif
#endif

#include <X11/Xutil.h>
#include <X11/cursorfont.h>
#include <Xm/Protocols.h>

#include <X11/Xresource.h>
#include "tooltip.h"

#ifndef XPM_OK
# define XPM_OK 0
#endif

#if XPM_OK
#define M_PIXEL_DATA "                                                ", \
                     "                                                ", \
                     "                                                ", \
                     "                        ++                      ", \
                     "                        ++                      ", \
                     "                       ++++                     ", \
                     "                      ++++++                    ", \
                     "                      ++++++                    ", \
                     "                      ++++++                    ", \
                     "                      ++++++                    ", \
                     "                      ++++++                    ", \
                     "                      ++++++                    ", \
                     "                     ++++++++                   ", \
                     "                     ++++++++                   ", \
                     "                     ++++++++                   ", \
                     "                     ++++++++                   ", \
                     "                     ++++++++                   ", \
                     "                     ++++++++                   ", \
                     "                    ++++++++++                  ", \
                     "                    ++++++++++                  ", \
                     "                   ++++++++++++                 ", \
                     "                   ++++++++++++                 ", \
                     "                 ++++++++++++++++               ", \
                     "                ++++++++++++++++++              ", \
                     "               ++++++++++++++++++++             ", \
                     "              ++++++++++++++++++++++            ", \
                     "             ++++++++++++++++++++++++           ", \
                     "             ++++++++++++++++++++++++           ", \
                     "            ++++++++++++++++++++++++++          ", \
                     "            ++++++++++++++++++++++++++          ", \
                     "            ++++++++++++++++++++++++++          ", \
                     "           +++++++++++++++++@#.#@++++++         ", \
                     "           ++++++++++++++++@.....@+++++         ", \
                     "           ++++++++++++++++#.....#+++++         ", \
                     "            +++++++++++++++.......++++          ", \
                     "            +++++++++++++++#.....#++++          ", \
                     "            +++++++++++++++@.....@++++          ", \
                     "             +++++++++++++++@#.#@++++           ", \
                     "             ++++++++++++++++++++++++           ", \
                     "              ++++++++++++++++++++++            ", \
                     "               ++++++++++++++++++++             ", \
                     "                ++++++++++++++++++              ", \
                     "                 ++++++++++++++++               ", \
                     "                   ++++++++++++                 ", \
                     "                       $++$                     ", \
                     "                                                ", \
                     "                                                ", \
                     "                                                "

char *drop_low[] =
{

"48 48 6 1",
"  c red m white",
". c #FFFFFF",
"+ c #000000",
"@ c #646464",
"# c #D5D5D5",
"$ c #8C8C8C",
M_PIXEL_DATA
};

char *drop_warn[] =
{

"48 48 6 1",
"  c orange m white",
". c #FFFFFF",
"+ c #000000",
"@ c #646464",
"# c #D5D5D5",
"$ c #8C8C8C",
M_PIXEL_DATA
};

char *drop_ok[] =
{

"48 48 6 1",
"  c green m white",
". c #FFFFFF",
"+ c #000000",
"@ c #646464",
"# c #D5D5D5",
"$ c #8C8C8C",
M_PIXEL_DATA
};
#endif

static Widget topLevel2;

static Pixmap pmLow, pmWarn, pmHigh;
static Widget low_PB;
static Widget warn_PB;
static Widget high_PB;
static Widget mainTop;
static int mapTop = 0;
static XtAppContext theApp;
static Cursor cursor = 0;

/*******************************************************************/
/* Function chgIcon()                                              */
/*                                                                 */
/* modifiy the pixmap for our "icon window"                        */
/*                                                                 */
/* Input: Widget parent not used                                   */
/*        int   *next   not used                                   */
/*                                                                 */
/* return: -                                                       */
/*                                                                 */
/*******************************************************************/

void chgIcon(int val)
{
#if XPM_OK
   static int oldVal = 0;

   if ( topLevel2 == NULL )
      return;
   if ( val == oldVal )
      return;
   switch (val)
   {
      case 0:
         XtUnmanageChild(high_PB);
         XtUnmanageChild(warn_PB);
         XtManageChild(low_PB);
         break;
      case 1:
         val = 0;
         XtUnmanageChild(high_PB);
         XtUnmanageChild(low_PB);
         XtManageChild(warn_PB);
         break;
      case 2:
         XtUnmanageChild(low_PB);
         XtUnmanageChild(warn_PB);
         XtManageChild(high_PB);
   }
   oldVal = val;
#endif
}

/*******************************************************************/
/* Function map_CB()                                               */
/*                                                                 */
/* Map the main window                                             */
/*                                                                 */
/* Input: Widget w not used                                        */
/*        XtPointer client data really the main top level          */
/*        XtPointer callData not used                              */
/*                                                                 */
/* return: -                                                       */
/*                                                                 */
/*******************************************************************/

static void map_CB(Widget w, XtPointer clientData, XtPointer callData)
{
   Widget topLevel = (Widget)clientData;
   
   XWindowAttributes attr;
   XGetWindowAttributes(XtDisplay(topLevel), XtWindow(topLevel), &attr);

   if ( attr.map_state == IsUnmapped )
   {
      mapTop = 1;
      XMapRaised(XtDisplay(topLevel),XtWindow(topLevel));
   }
   else
   {
      mapTop = 0;
      XtUnmapWidget(topLevel);
   }
}

/*******************************************************************/
/* Function mEvHdl()                                               */
/*                                                                 */
/* Avoid unmap for the main window                                 */
/*                                                                 */
/* Input: Widget w not used                                        */
/*        XtPointer closure  not used                              */
/*        XEvent   *ev        the event                            */
/*        Boolean  *cont      the event                            */
/*                                                                 */
/* return: -                                                       */
/*                                                                 */
/*******************************************************************/

static void mEvHdl(Widget w, XtPointer closure, XEvent *ev, Boolean *cont)
{
   if ( mapTop && ev->type == UnmapNotify)
   {
      XMapRaised(XtDisplay(w),XtWindow(w));
   }
}

/*******************************************************************/
/* Function mEvHdl2()                                              */
/*                                                                 */
/* Avoid unmap for the "icon" window                               */
/*                                                                 */
/* Input: Widget w not used                                        */
/*        XtPointer closure  not used                              */
/*        XEvent   *ev        the event                            */
/*        Boolean  *cont      the event                            */
/*                                                                 */
/* return: -                                                       */
/*                                                                 */
/*******************************************************************/

static void mEvHdl2(Widget w, XtPointer closure, XEvent *ev, Boolean *cont)
{
   if ( ev->type == UnmapNotify)
   {
      XMapRaised(XtDisplay(w),XtWindow(w));
   }
}

/*******************************************************************/
/* Function createGC()                                             */
/*                                                                 */
/* create a GC for the move  function of our "icon" window         */
/*                                                                 */
/* Input: Widget w                                                 */
/*                                                                 */
/* return: GC                                                      */
/*                                                                 */
/*******************************************************************/

static GC createGC(Widget w)
{
   static GC gc = NULL;
   XGCValues gcVal;
   Display *dpy = XtDisplay(w);
   Window root = XDefaultRootWindow(dpy);
   int    screen = XDefaultScreen(dpy);
   unsigned long valueMask;
   
   if ( gc ) return gc;
   valueMask = GCFunction | GCPlaneMask | GCForeground | GCBackground | GCLineWidth;
   gcVal.function = GXxor;
   gcVal.plane_mask = BlackPixel(dpy,screen) ^ WhitePixel(dpy, screen);
   gcVal.foreground = 0xffffffff;
   gcVal.background = 0;
   gcVal.line_width = 0;
   gc = XCreateGC(dpy,root,valueMask, &gcVal);
   XSetSubwindowMode(dpy,gc,IncludeInferiors);
   return gc;
}

/*******************************************************************/
/* Function cmvHdl()                                               */
/*                                                                 */
/* Handle the Event for the move function of our "icon" window     */
/*                                                                 */
/* Input: Widget w            which widget                         */
/*        XtPointer closure   not important                        */
/*        XEvent *ev          event                                */
/*        Boolean *cont       not important                        */
/*                                                                 */
/* return: -                                                       */
/*                                                                 */
/*******************************************************************/

static void mvHdl(Widget w, XtPointer closure, XEvent *ev, Boolean *cont)
{
   GC gc;
   static int dx;
   static int dy;
   static int ox;
   static int oy;
   if ( ev->type == ButtonPress && 
        (ev->xbutton.button == 3 || ev->xbutton.button == 2 ) )
   {
      /* set cursor */
      if ( !cursor )
         cursor = XCreateFontCursor(XtDisplay(w), XC_fleur);
      XDefineCursor(XtDisplay(w),XtWindow(w), cursor);
      /* Draw the first rubberband */
      gc = createGC(w);
      XDrawRectangle(XtDisplay(w),XDefaultRootWindow(XtDisplay(w)), gc,
                     ox=ev->xbutton.x_root-ev->xbutton.x, 
                     oy=ev->xbutton.y_root-ev->xbutton.y,
                     48,48);
      dx = ev->xbutton.x;
      dy = ev->xbutton.x;
   }
   if ( ev->type == MotionNotify  )
   {
      gc = createGC(w);
      XDrawRectangle(XtDisplay(w),XDefaultRootWindow(XtDisplay(w)), gc,
                     ox, 
                     oy,
                     48,48);
      XDrawRectangle(XtDisplay(w),XDefaultRootWindow(XtDisplay(w)), gc,
                     ox=ev->xbutton.x_root-dx, 
                     oy=ev->xbutton.y_root-dy,
                     48,48);
   }
   if ( ev->type == ButtonRelease && 
        (ev->xbutton.button == 3 || ev->xbutton.button == 2 )  )
   {
      gc = createGC(w);
      XDrawRectangle(XtDisplay(w),XDefaultRootWindow(XtDisplay(w)), gc,
                     ox, 
                     oy,
                     48,48);
      XMoveWindow(XtDisplay(topLevel2), XtWindow(topLevel2), ox,oy);
      XRaiseWindow(XtDisplay(topLevel2), XtWindow(topLevel2));
      XUndefineCursor(XtDisplay(w),XtWindow(w));
   }

}

/*******************************************************************/
/* Function createPixmaps()                                        */
/*                                                                 */
/* Create all pixmaps we use                                       */
/*                                                                 */
/* Input: Widget w need display and a window                       */
/*                                                                 */
/* return: -                                                       */
/*                                                                 */
/*******************************************************************/

static void createPixmaps(Widget w)
{
#if XPM_OK
   Display       *dsp = XtDisplay(w);
   Window         win = RootWindowOfScreen(XtScreen(w));
   XpmAttributes  attributes;
   Pixmap         shapemask;
   int            ret;
   
   memset(&attributes, 0, sizeof(attributes));
   attributes.valuemask = 0;
   
   ret  = XpmCreatePixmapFromData(dsp, win, drop_low, &pmLow,  &shapemask, &attributes);
   attributes.valuemask = 0;
   ret |= XpmCreatePixmapFromData(dsp, win, drop_warn, &pmWarn, &shapemask, &attributes);
   attributes.valuemask = 0;
   ret |= XpmCreatePixmapFromData(dsp, win, drop_ok, &pmHigh, &shapemask, &attributes);
   /* for SuSE 10.0 bug */
   if ( ret != 0 )
      pmLow = pmWarn = pmHigh = (Pixmap) 0;
#endif   
}

/*******************************************************************/
/* Function popupIcon()                                            */
/*                                                                 */
/* pop pup the icon window and popdown main window                 */
/*                                                                 */
/* Input: -                                                        */
/*                                                                 */
/* return: -                                                       */
/*                                                                 */
/*******************************************************************/
static int firstTime = 1;

void popupIcon(int x, int y)
{
#if XPM_OK
   if ( topLevel2 )
   {
       /* unmap the toplevel window if mapped */
       mapTop=0;
       XUnmapWindow(XtDisplay(mainTop), XtWindow(mainTop));

       /* map the "icon" window */
       XtPopup(topLevel2, XtGrabNonexclusive);

       if ( firstTime )
       {
          XMoveWindow(XtDisplay(topLevel2), XtWindow(topLevel2),x,y);
          firstTime = 0;
       }
    }
    else
    {
       mapTop = 0;
    }
#endif
}

/*******************************************************************/
/* Function unmapIcon()                                            */
/*                                                                 */
/* pop pup the icon window and popdown main window                 */
/*                                                                 */
/* Input: -                                                        */
/*                                                                 */
/* return: -                                                       */
/*                                                                 */
/*******************************************************************/

void unmapIcon()
{
   if ( topLevel2 )
   {
      XMapWindow(XtDisplay(mainTop), XtWindow(mainTop));
      XtDestroyWidget(topLevel2);
      firstTime = 0;
      topLevel2 = NULL;
   }
}

/*******************************************************************/
/* Function createIconLayout()                                     */
/*                                                                 */
/* Create our "Icon" window and resgister all event handlers,...   */
/*                                                                 */
/* Input: Widget top, the main toplevel                            */
/*        XtAppContext app we need it                              */
/*        char *mainResource,  class name                          */
/*        char *lang name of main form for this window             */
/*        int   popup if true popup the icon window, popdow the    */
/*                    main window                                  */
/*                                                                 */
/* return: -                                                       */
/*                                                                 */
/*******************************************************************/

Widget createIconLayout(Widget top, XtAppContext app, char *mainResource, char *lang, int popup)
{
#if XPM_OK
   Widget mainWindow2;
   theApp = app;
   mainTop = top;

   if ( topLevel2 )
      return topLevel2;

   /* create a second top level, avoid close */
   topLevel2 = XtVaAppCreateShell(mainResource, mainResource,
                                  topLevelShellWidgetClass,
                                  XtDisplay(top),
                                  XmNmwmDecorations, 0,
                                  XmNdeleteResponse, XmDO_NOTHING,
                                  NULL);

   /* create the amin window */
   mainWindow2 = XtVaCreateWidget(lang,
                                  xmFormWidgetClass,
                                  topLevel2,
                                  XmNheight,       48,
                                  XmNwidth,        48,
                                  NULL);

   XtManageChild(mainWindow2);
   
   /* create the needed piymaps */
   createPixmaps(topLevel2);

   /* create 3 push button according to the state we want */
   if ( pmLow != (Pixmap)0 )
   {
       low_PB = XtVaCreateWidget("low_PB",
                                 xmPushButtonWidgetClass,
                                 mainWindow2,
                                 XmNbackgroundPixmap, pmLow,
                                 XmNarmPixmap,        pmLow,
                                 XmNlabelType,        XmPIXMAP,
                                 NULL);

       warn_PB = XtVaCreateWidget("warn_PB",
                                  xmPushButtonWidgetClass,
                                  mainWindow2,
                                  XmNbackgroundPixmap, pmWarn,
                                  XmNarmPixmap,        pmWarn,
                                  XmNlabelType,        XmPIXMAP,
                                  NULL);

       high_PB = XtVaCreateWidget("high_PB",
                                  xmPushButtonWidgetClass,
                                  mainWindow2,
                                  XmNbackgroundPixmap, pmHigh,
                                  XmNarmPixmap,        pmHigh,
                                  XmNlabelType,        XmPIXMAP,
                                  NULL);
   }
   else /* and work around for buggy SuSE 10.0 */
   {
       XmString xms = XmStringCreateSimple(" ! ");

       low_PB = XtVaCreateWidget("low_PB",
                                 xmPushButtonWidgetClass,
                                 mainWindow2,
                                 XmNlabelString, xms,
                                 NULL);
       XtVaSetValues(low_PB, XtVaTypedArg, XmNbackground,XmRString,"red",8,NULL);

       warn_PB = XtVaCreateWidget("warn_PB",
                                  xmPushButtonWidgetClass,
                                  mainWindow2,
                                  XmNlabelString, xms,
                                  NULL);
       XtVaSetValues(warn_PB, XtVaTypedArg, XmNbackground,XmRString,"orange",8,NULL);

       high_PB = XtVaCreateWidget("high_PB",
                                  xmPushButtonWidgetClass,
                                  mainWindow2,
                                  XmNlabelString, xms,
                                  NULL);
       XtVaSetValues(high_PB, XtVaTypedArg, XmNbackground,XmRString,"green",8,NULL);
       XmStringFree(xms);
   }
   
   /* This for Lesstif, the size must be set after the widget  */
   /* was created, motif openMotif allow to pass the values at */
   /* creation                                                 */
   XtVaSetValues(low_PB, XmNheight,48,XmNwidth,48,NULL);
   XtVaSetValues(warn_PB,XmNheight,48,XmNwidth,48,NULL);
   XtVaSetValues(high_PB,XmNheight,48,XmNwidth,48,NULL);

   /* manage only one push button (error PB) */
   XtManageChild(low_PB);

   /* add tooltip handling */
   xmAddTooltip(low_PB);
   xmAddTooltip(warn_PB);
   xmAddTooltip(high_PB);
   
   /* add the callbacks so we can react on a release event */
   XtAddCallback(low_PB,  XmNactivateCallback, map_CB, top);
   XtAddCallback(warn_PB, XmNactivateCallback, map_CB, top);
   XtAddCallback(high_PB, XmNactivateCallback, map_CB, top);

   /* for KDE .... ! */
   /* make sure that the main window will not become an icon */
   XtAddEventHandler(top,
                     VisibilityChangeMask|StructureNotifyMask|PropertyChangeMask,
                     True, mEvHdl, NULL);

   /* and for ourself */
   XtAddEventHandler(topLevel2,
                     VisibilityChangeMask|StructureNotifyMask|PropertyChangeMask,
		     True, mEvHdl2, NULL);

   /* Add the event handler for the move function */
   XtAddEventHandler(low_PB,
                     ButtonPressMask|ButtonReleaseMask|Button3MotionMask|Button2MotionMask,
                     True, mvHdl, NULL);
   XtAddEventHandler(warn_PB,
                     ButtonPressMask|ButtonReleaseMask|Button3MotionMask|Button2MotionMask,
                     True, mvHdl, NULL);
   XtAddEventHandler(high_PB,
                     ButtonPressMask|ButtonReleaseMask|Button3MotionMask|Button2MotionMask,
                     True, mvHdl, NULL);

   /* we will be visible now */
   if ( popup )
   {
      XtPopup(topLevel2, XtGrabNonexclusive);
      /* unmap the toplevel window if mapped */
      XUnmapWindow(XtDisplay(top), XtWindow(top));
   } 

   return topLevel2;
#else
   return NULL;
#endif
}

/*******************************************************************/
/* Function iconAddTooltip()                                       */
/*                                                                 */
/* Replace/Add the given text to the resource database             */
/*                                                                 */
/* Input: char *text the text to add for the pushbutton tooltip    */
/*                                                                 */
/* return: -                                                       */
/*                                                                 */
/*******************************************************************/


void iconAddTooltip(char *text)
{
#if XPM_OK
   XrmDatabase database;
   char        value[1024];
   if ( topLevel2 )
   {
      database = XtDatabase(XtDisplay(topLevel2));

      sprintf(value, "*high_PB.tooltip: %s", text);
      XrmPutLineResource(&database, value);
      sprintf(value, "*low_PB.tooltip: %s", text);
      XrmPutLineResource(&database, value);
      sprintf(value, "*warn_PB.tooltip: %s", text);
      XrmPutLineResource(&database, value);
   }
#endif
}

/*******************************************************************/
/* Function iconAskPos()                                           */
/*                                                                 */
/* Get the position of the "icon" window so we cann place it       */
/* at the right position later                                     */
/*                                                                 */
/* Output int *x, *y                                               */
/*                                                                 */
/* return: True if value retriewed                                 */
/*                                                                 */
/*******************************************************************/

int iconAskPos(int *x, int *y)
{
#if XPM_OK
   Display *dpy;
   Window  window;
   XWindowAttributes attr;
   int rx,ry;
   Window rwin;

   if ( topLevel2 && XtIsRealized(topLevel2) )
   {
      dpy = XtDisplay(topLevel2);
      window  = XtWindow(topLevel2);
      XGetWindowAttributes(dpy, window, &attr);

      XTranslateCoordinates(dpy, window, attr.root,
                            -attr.border_width,
                            -attr.border_width,
                            &rx, &ry, &rwin);

      if ( rx == attr.x && ry == attr.y )
      {
         *x = rx;
         *y = ry;
      }
      else
      {
         *x = rx - attr.x;
         *y = ry - attr.y;
      }

      return True;
   }
#endif
   return False;
}
