/**************************************************************************

    This file is part of Xclass95, a Win95-looking GUI toolkit.
    Copyright (C) 1996, 1997 David Barth, Ricky Ralston, Hector Peraza.

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library 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
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public
    License along with this library; if not, write to the Free
    Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

**************************************************************************/

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
#include <sys/stat.h>

#include <xclass/utils.h>
#include <xclass/OXMsgBox.h>
#include <xclass/OXFileDialog.h>


#define IDF_CDUP        0
#define IDF_NEW_FOLDER  1
#define IDF_LIST        2
#define IDF_DETAILS     3

#define IDF_FSLB        4
#define IDF_FTYPESLB    5


static char *defaultFiletypes[] = { "All files",      "*",
                                    "Document files", "*.doc",
                                    "Text files",     "*.txt",
                                    NULL,             NULL };


//--------------------------------------------------------------

OFileInfo::OFileInfo() { 
  filename = NULL;
  ini_dir = NULL;
  file_types = NULL;
}
  
OFileInfo::~OFileInfo() { 
  if (filename) delete[] filename;
  if (ini_dir) delete[] ini_dir;
}


//--------------------------------------------------------------

OXFileDialog::OXFileDialog(const OXWindow *p, const OXWindow *main,
                           int dlg_type, OFileInfo *file_info) :
  OXTransientFrame(p, main, 10, 10, MAIN_FRAME | VERTICAL_FRAME) {
    int  i, ax, ay;
    char path[PATH_MAX];
    Window wdummy;
    const  OPicture *pcdup, *pnewf, *plist, *pdetails;
    char *lookinString;
    char *okButtonString;
    char *windowNameString;

    _file_info = file_info;

    if (!_file_info->file_types)
      _file_info->file_types = defaultFiletypes;

    _htop = new OXHorizontalFrame(this, 10, 10);

    _lmain = new OLayoutHints(LHINTS_TOP | LHINTS_EXPAND_X, 4, 4, 3, 1);

    //--- top toolbar elements

    switch(dlg_type) {
      default:
      case FDLG_OPEN:
        lookinString     = "L&ook in";
        okButtonString   = "&Open";
        windowNameString = "Open";
        break;

      case FDLG_SAVE:
        lookinString     = "S&ave in";
        okButtonString   = "&Save";
        windowNameString = "Save as...";
        break;

      case FDLG_BROWSE:
        lookinString     = "L&ook in";
        okButtonString   = "&Ok";
        windowNameString = "Browse";
        break;
     }

    _lookin = new OXLabel(_htop, new OHotString(lookinString));
    _tree_lb = new OXFileSystemDDListBox(_htop, IDF_FSLB);
    _tree_lb->Associate(this);

    pcdup = _client->GetPicture("tb-uplevel.xpm");
    pnewf = _client->GetPicture("tb-newfolder.xpm");
    plist = _client->GetPicture("tb-list.xpm");
    pdetails = _client->GetPicture("tb-details.xpm");

    if (!(plist && pnewf && plist && pdetails))
      FatalError("OXFileDialog: missing toolbar pixmap(s).\n");

    _cdup = new OXPictureButton(_htop, pcdup, IDF_CDUP);
    _newf = new OXPictureButton(_htop, pnewf, IDF_NEW_FOLDER);
    _list = new OXPictureButton(_htop, plist, IDF_LIST);
    _details = new OXPictureButton(_htop, pdetails, IDF_DETAILS);

    _cdup->Associate(this);
    _newf->Associate(this);
    _list->Associate(this);
    _details->Associate(this);

    _list->AllowStayDown(True);
    _details->AllowStayDown(True);

    _lhl = new OLayoutHints(LHINTS_LEFT | LHINTS_CENTER_Y,  5,  5, 2, 2); 
    _lht = new OLayoutHints(LHINTS_LEFT | LHINTS_EXPAND_Y, 10,  0, 2, 2); 
    _lb1 = new OLayoutHints(LHINTS_LEFT | LHINTS_CENTER_Y,  8,  0, 2, 2); 
    _lb2 = new OLayoutHints(LHINTS_LEFT | LHINTS_CENTER_Y,  0, 15, 2, 2);

    _tree_lb->Resize(200, _tree_lb->GetDefaultHeight());

    _htop->AddFrame(_lookin, _lhl);
    _htop->AddFrame(_tree_lb, _lht); 
    _htop->AddFrame(_cdup, _lb1);
    _htop->AddFrame(_newf, _lb1);
    _htop->AddFrame(_list, _lb1);
    _htop->AddFrame(_details, _lb2);

    AddFrame(_htop, _lmain);

    //--- file view

    _fv = new OXListView(this, 400, 161);

    _fc = new OXFileContainer(_fv->GetViewPort(),
                              _file_info->MimeTypesList, NULL,
                              10, 10, HORIZONTAL_FRAME, _whitePixel);
    _fc->Associate(this);

    _fv->GetViewPort()->SetBackgroundColor(_whitePixel);
    _fv->SetContainer(_fc);

    if (_file_info->ini_dir) {
      _fc->ChangeDirectory(_file_info->ini_dir);
      delete[] _file_info->ini_dir;  // in case ChangeDirectory fails...
    } else {
      _fc->ChangeDirectory(".");
    }

    _file_info->ini_dir = new char[strlen(path)+1];
    strcpy(_file_info->ini_dir, path);
    _tree_lb->UpdateContents(getcwd(path, PATH_MAX));

    _fv->SetViewMode(LV_LIST);
    _fc->Sort(SORT_BY_TYPE);

    _list->SetState(BUTTON_ENGAGED);

    AddFrame(_fv, _lmain);

    //--- file name and types

    _hf = new OXHorizontalFrame(this, 10, 10);

    _vf = new OXVerticalFrame(_hf, 10, 10);
    _lvf = new OLayoutHints(LHINTS_LEFT | LHINTS_CENTER_Y |
                            LHINTS_EXPAND_X);

    _hfname = new OXHorizontalFrame(_vf, 10, 10);

    _lfname = new OXLabel(_hfname, new OHotString("File &name:"));
    _tbfname = new OTextBuffer(PATH_MAX+10);
    _fname = new OXTextEntry(_hfname, _tbfname);
    _fname->Resize(220, _fname->GetDefaultHeight());

    _lht1 = new OLayoutHints(LHINTS_RIGHT | LHINTS_CENTER_Y, 0, 20, 2, 2); 

    _hfname->AddFrame(_lfname, _lhl);
    _hfname->AddFrame(_fname, _lht1);

    _vf->AddFrame(_hfname, _lvf);

    _hftype = new OXHorizontalFrame(_vf, 10, 10);

    _lftypes = new OXLabel(_hftype, new OHotString("Files of &type:"));
    _ftypes = new OXDDListBox(_hftype, IDF_FTYPESLB);
    _ftypes->Associate(this);
    _ftypes->Resize(220, _fname->GetDefaultHeight());

    if (_file_info->file_types) {
      for (i=0; _file_info->file_types[i] != NULL; i+=2)
        _ftypes->AddEntry(new OString(_file_info->file_types[i]), i);
      _ftypes->Select(0);

      _tbfname->Clear();
      _tbfname->AddText(0, _file_info->file_types[1]);
      _fc->SetFileFilter(_file_info->file_types[1]);
    }

    _hftype->AddFrame(_lftypes, _lhl);
    _hftype->AddFrame(_ftypes, _lht1);

    _vf->AddFrame(_hftype, _lvf);

    _hf->AddFrame(_vf, _lvf);

    //--- Open/Save and Cancel buttons

    _vbf = new OXVerticalFrame(_hf, 10, 10, FIXED_WIDTH);
    _lvbf = new OLayoutHints(LHINTS_LEFT | LHINTS_CENTER_Y);

    _lb = new OLayoutHints(LHINTS_TOP | LHINTS_EXPAND_X, 0, 0, 2, 2);

    _ok = new OXTextButton(_vbf, new OHotString(okButtonString), ID_OK);
    _cancel = new OXTextButton(_vbf, new OHotString("Cancel"), ID_CANCEL);

    _ok->Associate(this);
    _cancel->Associate(this);

    _vbf->AddFrame(_ok, _lb);
    _vbf->AddFrame(_cancel, _lb);

    int width = max(_ok->GetDefaultWidth(), _cancel->GetDefaultWidth()) + 20;
    _vbf->Resize(width, _vbf->GetDefaultHeight());

    _hf->AddFrame(_vbf, _lvbf);

    AddFrame(_hf, _lmain);

    MapSubwindows();

    ODimension size = GetDefaultSize();

    Resize(size);

    //---- position relative to the parent's window

    if (main) {
      XTranslateCoordinates(GetDisplay(),
                            main->GetId(), GetParent()->GetId(),
                            (((OXFrame *) main)->GetWidth() - _w) >> 1,
                            (((OXFrame *) main)->GetHeight() - _h) >> 1,
                            &ax, &ay, &wdummy);
      if (ax < 0) ax = 10;
      Move(ax, ay);
      SetWMPosition(ax, ay);
    }
    
    //---- make the message box non-resizable

    SetWMSize(size.w, size.h);
    SetWMSizeHints(size.w, size.h, size.w, size.h, 0, 0);

    SetWindowName(windowNameString);
    SetIconName(windowNameString);
    SetClassHints("FileDialog", "FileDialog");

    SetMWMHints(MWM_DECOR_ALL | MWM_DECOR_RESIZEH | MWM_DECOR_MAXIMIZE |
                                MWM_DECOR_MINIMIZE | MWM_DECOR_MENU,
                MWM_FUNC_ALL | MWM_FUNC_RESIZE | MWM_FUNC_MAXIMIZE |
                               MWM_FUNC_MINIMIZE,
                MWM_INPUT_MODELESS);

    MapWindow();
    _client->WaitFor(this);
}

OXFileDialog::~OXFileDialog() {
  delete _ok; delete _cancel;
  delete _vbf;
  delete _lb; delete _lvbf;

  delete _lfname; delete _fname;
  delete _hfname;

  delete _lftypes; delete _ftypes;
  delete _hftype;

  delete _vf;
  delete _hf;

  delete _fv; delete _fc;

  delete _cdup; delete _newf; delete _list; delete _details;
  delete _tree_lb; delete _lookin;

  delete _lb1; delete _lb2; delete _lhl;
  delete _lht; delete _lht1; delete _lvf;

  delete _htop;
  delete _lmain;
}

void OXFileDialog::CloseWindow() {
  if (_file_info->filename) delete[] _file_info->filename;
  _file_info->filename = NULL;
  delete this;
}

int OXFileDialog::ProcessMessage(long msg, long parm1, long parm2) {
  int sel;
  OXTreeLBEntry *e;
  OXTextLBEntry *te;
  OXFileItem *f;
  char path[PATH_MAX];
  void *p = NULL;
  struct stat sbuf;   

  switch (GET_MSG(msg)) {
  case XC_COMMAND:
    switch (GET_SUBMSG(msg)) { 
    case CM_BUTTON:
      switch (parm1) {
      case ID_OK:
        if (stat(_tbfname->GetString(), &sbuf) == 0 && S_ISDIR(sbuf.st_mode)) {
          _fc->ChangeDirectory(_tbfname->GetString());
          _tree_lb->UpdateContents(getcwd(path, PATH_MAX));
          if (_file_info->ini_dir) delete[] _file_info->ini_dir;
          _file_info->ini_dir = new char[strlen(path)+1];
          strcpy(_file_info->ini_dir, path);
        } else {
          if (_file_info->filename) delete[] _file_info->filename;
          _file_info->filename = new char[_tbfname->GetTextLength()+1];
          strcpy(_file_info->filename, _tbfname->GetString());
          delete this;
        }
        break;

      case ID_CANCEL:
        if (_file_info->filename) delete[] _file_info->filename;
        _file_info->filename = NULL;
        delete this;
        break;

      case IDF_CDUP:
        _fc->ChangeDirectory("..");
        _tree_lb->UpdateContents(getcwd(path, PATH_MAX));
        if (_file_info->ini_dir) delete[] _file_info->ini_dir;
        _file_info->ini_dir = new char[strlen(path)+1];
        strcpy(_file_info->ini_dir, path);
        break;

      case IDF_NEW_FOLDER:
        break;

      case IDF_LIST:
        _fv->SetViewMode(LV_LIST);
        _details->SetState(BUTTON_UP);
        break;

      case IDF_DETAILS:
        _fv->SetViewMode(LV_DETAILS);
        _list->SetState(BUTTON_UP);
        break;
      }
      break;

    case CM_DDLISTBOX:
      switch (parm1) {
      case IDF_FSLB:
        e = (OXTreeLBEntry *) _tree_lb->GetSelectedEntry();
        if (e) {
          _fc->ChangeDirectory(e->GetPath()->GetString());
          _tree_lb->UpdateContents(getcwd(path, PATH_MAX));
          if (_file_info->ini_dir) delete[] _file_info->ini_dir;
          _file_info->ini_dir = new char[strlen(path)+1];
          strcpy(_file_info->ini_dir, path);
        }
        break;

      case IDF_FTYPESLB:
        te = (OXTextLBEntry *) _ftypes->GetSelectedEntry();
        if (te) {
          _tbfname->Clear();
          _tbfname->AddText(0, _file_info->file_types[te->ID()+1]);
          _fc->SetFileFilter(_file_info->file_types[te->ID()+1]);
          _client->NeedRedraw(_fname);
        }
        break;
      }
      break;

    default:
      break;
    } // switch(GET_SUBMSG(msg))
    break;

  case XC_CONTAINER:
    switch(GET_SUBMSG(msg)) {
    case CT_ITEMCLICK:
      if (parm1 == Button1) {
        if ((sel = _fc->NumSelected()) == 1) {
          _tbfname->Clear();
          _tbfname->AddText(0, _fc->GetNextSelected(&p)->GetName()->GetString());
          _client->NeedRedraw(_fname);
        }
      }
      break;

    case CT_ITEMDBLCLICK:
      if (parm1 == Button1) {
        if ((sel = _fc->NumSelected()) == 1) {
          f = (OXFileItem *) _fc->GetNextSelected(&p);
          if (S_ISDIR(f->GetType())) {
            _fc->ChangeDirectory(f->GetName()->GetString());
            _tree_lb->UpdateContents(getcwd(path, PATH_MAX));
            if (_file_info->ini_dir) delete[] _file_info->ini_dir;
            _file_info->ini_dir = new char[strlen(path)+1];
            strcpy(_file_info->ini_dir, path);
          } else {
            if (_file_info->filename) delete[] _file_info->filename;
            _file_info->filename = new char[f->GetName()->GetLength()+1];
            strcpy(_file_info->filename, f->GetName()->GetString());
            delete this;
          }
        }
      }
      break;

    default:
      break;

    } // switch(GET_SUBMSG(msg))
    break;

  default:
    break;

  } // switch(GET_MSG(msg))

  return True;
}
