/* A run program for Fvwm95 and the Xclass set.          */
/* Written by Frank Hall. April 4, 1997                  */
/* Modified May 14, 1997 to incorporate new OXFileDialog */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <X11/keysym.h>
#include <xclass/OXClient.h>
#include <xclass/OXWindow.h>
#include <xclass/OXFrame.h>
#include <xclass/OXWidgets.h>
#include <xclass/OXMsgBox.h>
#include <xclass/OString.h>
#include <xclass/OMimeTypes.h>
#include <xclass/OXFileDialog.h>
#include <xclass/utils.h>
#include <unistd.h>

#include "run.xpm"

#define MaxArgs          10

#define OKButtonID       1
#define CancelButtonID   2
#define BrowseButtonID   3
#define UseXTermID       4
#define RunTextEntryID   5

OMimeTypes *MimeTypeList;

char *filetypes[] = { "All Files", "*",
                      NULL,        NULL };

class RunTextEntry : public OXTextEntry {
public:
  RunTextEntry(const OXWindow * p, OTextBuffer * text, int ID) : 
    OXTextEntry(p, text, ID) {} 

  virtual int HandleKey(XKeyEvent * event);
};

class RunFrame : public OXMainFrame {
public:
  RunFrame(const OXWindow * p, int w, int h, unsigned long options);
  virtual ~RunFrame();

  virtual int HandleFocusChange(XFocusChangeEvent * event);
  virtual int ProcessMessage(long msg, long parm1, long parm2);

protected:
  OXCompositeFrame *DescriptionFrame;
  OXCompositeFrame *TextFrame;
  OXCompositeFrame *ButtonFrame;

  OTextBuffer *ProgramToRun;

  RunTextEntry *ProgramTextEntry;

  OXLabel *Description;
  OString *DescriptionText;
  OXIcon  *DescriptionIcon;

  OXLabel *OpenLabel;
  OString *OpenText;

  const OPicture *IconPicture;

  OHotString *UseXTermHotString;
  OHotString *OKHotString;
  OHotString *CancelHotString;
  OHotString *BrowseHotString;

  OXCheckButton *UseXTerm;
  OXTextButton *OKButton;
  OXTextButton *CancelButton;
  OXTextButton *BrowseButton;
};


OXClient *ClientX;
RunFrame *MainWindow;

main() 
{
  char mimerc[PATH_MAX];

  /* Create Client */
  ClientX = new OXClient;

  /* Get location of .mime.types file */
  sprintf(mimerc, "%s/.mime.types", getenv("HOME"));

  /* Initialize mime type list for browse option */
  MimeTypeList = new OMimeTypes(ClientX, mimerc);

  /* Create Main Window */
  MainWindow = new RunFrame(ClientX->GetRoot(), 250, 115, MAIN_FRAME | VERTICAL_FRAME);

  /* Map The Main Window */
  MainWindow->MapWindow();

  /* Activate The Whole Thing */
  ClientX->Run();

}


int RunTextEntry::HandleKey(XKeyEvent *event) {
  char tmp[10];
  KeySym keysym;
  XComposeStatus compose = { NULL, 0 };

  OXTextEntry::HandleKey(event);

  XLookupString(event, tmp, sizeof(tmp)-1, &keysym, &compose);

  if ((keysym == XK_Execute) || (keysym == XK_Return)) {
    SendMessage(_msgWindow, MK_MSG(XC_COMMAND, CM_BUTTON), OKButtonID, 0);
  } else if (keysym == XK_Escape) {
    SendMessage(_msgWindow, MK_MSG(XC_COMMAND, CM_BUTTON), CancelButtonID, 0);
  }

  return True;
}


RunFrame::RunFrame(const OXWindow *p, int w, int h, unsigned long options) :
  OXMainFrame(p, w, h, options) {
    int dummy;
    unsigned int ScreenHeight, udummy;
    Window wdummy;

    /* Track Focus Changes */
    XSelectInput(GetDisplay(), _id, FocusChangeMask | ExposureMask |
                 StructureNotifyMask);

    /* Create Frame To Hold Description */
    DescriptionFrame = new OXCompositeFrame(this, 60, 20, HORIZONTAL_FRAME);

    /* Get Picture From Pool */
    IconPicture = _client->GetPicture("run.xpm", run_xpm);

    /* Create Icon */
    DescriptionIcon = new OXIcon(DescriptionFrame, IconPicture, 32, 32);

    /* Attach Icon To Description Frame */
    DescriptionFrame->AddFrame(DescriptionIcon, new OLayoutHints(LHINTS_LEFT,
                                                     10, 10, 10, 10));

    /* Create Text For Label */
    DescriptionText = new OString("Please enter the program to be run.");

    /* Create Label */
    Description = new OXLabel(DescriptionFrame, DescriptionText);

    /* Attach Label To Description Frame */
    DescriptionFrame->AddFrame(Description, new OLayoutHints(LHINTS_LEFT,
                                                 10, 10, 17, 10));


    /* Create Frame To Hold Text Box */
    TextFrame = new OXCompositeFrame(this, 60, 20, HORIZONTAL_FRAME);

    /* Create Text For Open Label */
    OpenText = new OString("Open:");

    /* Create Label */
    OpenLabel = new OXLabel(TextFrame, OpenText);

    /* Attach Label To Description Frame */
    TextFrame->AddFrame(OpenLabel, new OLayoutHints(LHINTS_LEFT,
                                        10, 10, 3, 10));

    /* Create Text Buffer For Text Box */
    ProgramToRun = new OTextBuffer(255);

    /* Create Text Box */
    ProgramTextEntry = new RunTextEntry(TextFrame, ProgramToRun, RunTextEntryID);
    ProgramTextEntry->Associate(this);

    /* Set The Length Of The Text Box */
    ProgramTextEntry->Resize(225, ProgramTextEntry->GetDefaultHeight());

    /* Attach Text Box To Text Frame */
    TextFrame->AddFrame(ProgramTextEntry, new OLayoutHints(LHINTS_TOP,
                                               10, 10, 0, 0));

    /* Create Frame To Hold Buttons */
    ButtonFrame = new OXCompositeFrame(this, 60, 20, HORIZONTAL_FRAME);

    /* Create Label For Check Box */
    UseXTermHotString = new OHotString("Use XTerm");

    /* Create Check Box */
    UseXTerm = new OXCheckButton(ButtonFrame, UseXTermHotString, UseXTermID);

    /* Attach Check Box To Button Frame */
    ButtonFrame->AddFrame(UseXTerm, new OLayoutHints(LHINTS_TOP | LHINTS_LEFT,
                                         10, 5, 7, 2));

    OKHotString = new OHotString("    OK    ");
    OKButton = new OXTextButton(ButtonFrame, OKHotString, OKButtonID);
    OKButton->Associate(this);
    OKButton->SetState(BUTTON_DISABLED);

    /* Attach Button To Button Frame */
    ButtonFrame->AddFrame(OKButton, new OLayoutHints(LHINTS_TOP | LHINTS_LEFT,
                                         0, 5, 7, 2));

    CancelHotString = new OHotString(" Cancel ");
    CancelButton = new OXTextButton(ButtonFrame, CancelHotString, CancelButtonID);
    CancelButton->Associate(this);
    ButtonFrame->AddFrame(CancelButton, new OLayoutHints(LHINTS_TOP | LHINTS_LEFT,
                                             5, 5, 7, 2));

    BrowseHotString = new OHotString(" Browse ");
    BrowseButton = new OXTextButton(ButtonFrame, BrowseHotString, BrowseButtonID);
    BrowseButton->Associate(this);
    ButtonFrame->AddFrame(BrowseButton, new OLayoutHints(LHINTS_TOP | LHINTS_LEFT,
                                             5, 5, 7, 2));

    /* Attach Description Frame To Main Window */
    AddFrame(DescriptionFrame, new OLayoutHints(LHINTS_TOP, 0, 0, 1, 0));

    /* Attach Text Frame To Main Window */
    AddFrame(TextFrame, new OLayoutHints(LHINTS_TOP, 0, 0, 1, 0));

    /* Attach Button Frame To Main Window */
    AddFrame(ButtonFrame, new OLayoutHints(LHINTS_TOP, 0, 0, 1, 10));



    /* Set The Name Displayed In The Title Bar */
    SetWindowName("Run...");

    /* Set class name and resource names */
    SetClassHints("Run", "Run");

    /* Map All Sub Windows (And Frames) Of The Main Window */
    MapSubwindows();

    /* Set The Size Of The Main Window To Something Decent */
    Resize(GetDefaultWidth(), GetDefaultHeight());

    XGetGeometry(GetDisplay(), _client->GetRoot()->GetId(), &wdummy,
                 &dummy, &dummy, &udummy, &ScreenHeight, &udummy, &udummy);

    /* Move to a decent position */
    Move(20, ScreenHeight - GetDefaultHeight() - 100);

    /* Tell the WM we want that position */
    SetWMPosition(20, ScreenHeight - GetDefaultHeight() - 100);
}

RunFrame::~RunFrame() {
  delete BrowseButton;
  delete CancelButton;
  delete OKButton;

  _client->FreePicture(IconPicture);

  delete OpenLabel;

  delete DescriptionIcon;
  delete Description;

  delete ProgramTextEntry;

  delete ButtonFrame;
  delete TextFrame;
  delete DescriptionFrame;
}

int RunFrame::HandleFocusChange(XFocusChangeEvent * event) {
  /* Set the Input Focus to the Text Entry Widget */
  if ((event->mode == NotifyNormal) && (event->detail != NotifyPointer) &&
      (event->type == FocusIn)) {
    XSetInputFocus(GetDisplay(), ProgramTextEntry->GetId(), RevertToParent, CurrentTime);
  }
  return True;
}

int RunFrame::ProcessMessage(long msg, long parm1, long parm2) {
  int  i, j, k, Slen;
  char errmsg[255];
  char P2Run[255];
  char *Parg[MaxArgs];
  char *c;
  char PDirectory[PATH_MAX];
  OFileInfo fi;

  switch (GET_MSG(msg)) {

  case XC_COMMAND:
    switch (GET_SUBMSG(msg)) {

    case CM_BUTTON:
      switch (parm1) {

      case OKButtonID:
	c = (char *) ProgramToRun->GetString();
	strcpy(P2Run, c);
	if (strlen(P2Run) > 0) {
	  Slen = strlen(P2Run);
	  for (i=0, j=0, k=0; (i<Slen) && (j<MaxArgs); i++) {
	    if (P2Run[i] == ' ') {
	      P2Run[i] = '\0';
	      Parg[j++] = &P2Run[k];
	      k = i + 1;
	    }
	  }

	  Parg[j++] = &P2Run[k];
	  for (; j<MaxArgs; j++) Parg[j] = NULL;

	  if (UseXTerm->GetState() == BUTTON_DOWN) {
	    j = execlp("xterm", " ", "-e", Parg[0], Parg[1], Parg[2], Parg[3], Parg[4], Parg[5], Parg[6], Parg[7], Parg[8], Parg[9], 0);
	  } else {
	    j = execlp(Parg[0], " ", Parg[1], Parg[2], Parg[3], Parg[4], Parg[5], Parg[6], Parg[7], Parg[8], Parg[9], 0);
	  }

	  if (j == -1) {
	    strcpy(errmsg, "Could not open ");
	    c = (char *) ProgramToRun->GetString();
	    strcat(errmsg, c);
	    strcat(errmsg, ".");
	    new OXMsgBox(this->GetParent(), this, new OString("Run Error"), new OString(errmsg), MB_ICONSTOP, 4);
	  }
	}
	break;

      case CancelButtonID:
	CloseWindow();
	break;

      case BrowseButtonID:
	fi.MimeTypesList = MimeTypeList;
	fi.file_types = filetypes;
	new OXFileDialog(ClientX->GetRoot(), this, FDLG_OPEN, &fi);
	if (fi.filename) {
	  getcwd(PDirectory, PATH_MAX - 1);
	  if (PDirectory[strlen(PDirectory) - 1] != '/')
            strcat(PDirectory, "/");
	  strcat(PDirectory, fi.filename);
	  ProgramToRun->Clear();
	  ProgramToRun->AddText(0, PDirectory);
	  OKButton->SetState(BUTTON_UP);
	}
	break;
      }
      break;

    }
    break;

  case XC_TEXTENTRY:
    switch (GET_SUBMSG(msg)) {
    case TE_TEXTCHANGED:
      OKButton->SetState(ProgramToRun->GetTextLength() ?  BUTTON_UP : BUTTON_DISABLED);
      break;
    }
    break;

  }

  return True;
}
