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

    This file is part of explorer95, a file manager for fvwm95.
    Copyright (C) 1996, 1997 David Barth, Hector Peraza.

    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., 675 Mass Ave, Cambridge, MA 02139, USA.

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

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
#include <time.h>
#include <grp.h>
#include <pwd.h>
#include <sys/types.h>
#include <sys/stat.h>

#include <dirent.h>

#include <xclass/OIniFile.h>
#include <xclass/OMimeTypes.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/OPicture.h>
#include <xclass/OXFileContainer.h>
#include <xclass/OXFileDialog.h>
#include <xclass/utils.h>

#include "OXNewClient.h"
#include "OXNewWidgets.h"
#include "OXOptions.h"
#include "OXProperties.h"
#include "RecycledFiles.h"

#include "main.h"


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

OXExplorerClient *clientX;
OXExplorerMainFrame *mainWindow;

OMimeTypes *MimeTypeList;
RecycledFiles *Recycled;

OXFileContainer *fileWindow;

char *AppName, *AppPath;
char *StartDir, *HomeDir;

int DispFullPath = False;
int NewBrowser   = False;

int ShowToolBar = False;
int ShowStatusBar = True;
int ViewMode = M_VIEW_LARGEICONS;
int SortMode = M_VIEW_ARRANGE_BYNAME;

int MainMode = EXPLORER_MODE;

struct _default_icon default_icon[] = {
  { "folder",   NULL, NULL },
  { "app",      NULL, NULL },
  { "doc",      NULL, NULL },
  { "slink",    NULL, NULL },
  { NULL,       NULL, NULL }
};

struct _tb_data tb_data[] = {
  { "tb-uplevel.xpm",  "Up one level",        False, 99, NULL  },
  { "",                "",                    False, -10, NULL },
  { "tb-mapntw.xpm",   "Mount file system",   False, -10, NULL },
  { "tb-dscntw.xpm",   "Unmount file system", False, -10, NULL },
  { "",                "",                    False, -10, NULL },
  { "tb-cut.xpm",      "Cut",                 False, -10, NULL },
  { "tb-copy.xpm",     "Copy",                False, -10, NULL },
  { "tb-paste.xpm",    "Paste",               False, -10, NULL },
  { "",                "",                    False, -10, NULL },
  { "tb-undo.xpm",     "Undo",                False, -10, NULL },
  { "",                "",                    False, -10, NULL },
  { "tb-delete.xpm",   "Delete",              False, M_FILE_DELETE,     NULL },
  { "tb-prop.xpm",     "Properties",          False, M_FILE_PROPS,      NULL },
  { "",                "",                    False, -10, NULL },
  { "tb-bigicons.xpm", "Large icons",         True,  M_VIEW_LARGEICONS, NULL },
  { "tb-smicons.xpm",  "Small icons",         True,  M_VIEW_SMALLICONS, NULL },
  { "tb-list.xpm",     "List",                True,  M_VIEW_LIST,       NULL },
  { "tb-details.xpm",  "Details",             True,  M_VIEW_DETAILS,    NULL },
  { NULL,              NULL,                  False, 0, NULL }
};


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

main(int argc, char *argv[]) {
  char *p;
  char explorerrc[PATH_MAX];
  char mimerc[PATH_MAX];
  char recyclerc[PATH_MAX];
  char recyclebin[PATH_MAX];
  int  i;

  AppPath = argv[0];

  if ((p = strrchr(AppPath, '/')) == NULL)
    AppName = AppPath;
  else
    AppName = ++p;

  if (argc > 1) {
    StartDir = argv[1];
    MainMode = FILE_MGR_MODE;
  } else {
    StartDir = ".";
    MainMode = EXPLORER_MODE;
  }

  HomeDir = getenv("HOME");

  if (!HomeDir) HomeDir = "/";

  clientX = new OXExplorerClient;

  //--- read rc types...

  sprintf(mimerc, "%s/.mime.types", HomeDir);
  MimeTypeList = new OMimeTypes((OXClient *) clientX, mimerc);

  sprintf(recyclerc, "%s/.xclass.recycle/.recycle.files", HomeDir);
  sprintf(recyclebin, "%s/.xclass.recycle", HomeDir);
  Recycled = new RecycledFiles(recyclebin, recyclerc);

  sprintf(explorerrc, "%s/.explorerrc", HomeDir);

  mainWindow = new OXExplorerMainFrame(clientX->GetRoot(), 400, 200);
  mainWindow->ReadIniFile(explorerrc);

  //--- load default pictures...

  for (i=0; default_icon[i].picname_prefix != NULL; ++i) {
    char *name;

    name = new char[20];
    sprintf(name, "%s.s.xpm", default_icon[i].picname_prefix);
    if ((default_icon[i].icon[0] = clientX->GetPicture(name)) == NULL)
      FatalError("Pixmap not found: %s", name);

    name = new char[20];
    sprintf(name, "%s.t.xpm", default_icon[i].picname_prefix);
    if ((default_icon[i].icon[1] = clientX->GetPicture(name)) == NULL)
      FatalError("Pixmap not found: %s", name);

  }

  fileWindow->ChangeDirectory(StartDir);

  mainWindow->UpdateListBox();
  mainWindow->UpdateTree();
  mainWindow->MapWindow();

  clientX->Run();
}

OXExplorerMainFrame::OXExplorerMainFrame(const OXWindow *p, int w, int h) :
  OXMainFrame(p, w, h) {
  char tmp[BUFSIZ];

  //-------------- create menu bar and popup menus

  _menuBarLayout = new OLayoutHints(LHINTS_TOP | LHINTS_LEFT | LHINTS_EXPAND_X, 0, 0, 1, 1);
  _menuBarItemLayout = new OLayoutHints(LHINTS_TOP | LHINTS_LEFT, 0, 4, 0, 0);

  sendToMenu = new OXPopupMenu(_client->GetRoot());
  sendToMenu->AddEntry(new OHotString("Fax Recipient"),  21, _client->GetPicture("fax.t.xpm"));
  sendToMenu->AddEntry(new OHotString("Mail Recipient"), 22, _client->GetPicture("mail.t.xpm"));
  sendToMenu->AddEntry(new OHotString("My Briefcase"),   23, _client->GetPicture("briefcase.t.xpm"));

  objectMenu = new OXPopupMenu(_client->GetRoot());
  objectMenu->AddEntry(new OHotString("&Open"),    1501);
  objectMenu->AddEntry(new OHotString("&Explore"), 1502);
  objectMenu->AddEntry(new OHotString("&Find..."), 1503);
  objectMenu->AddSeparator();
  objectMenu->AddPopup(new OHotString("Se&nd To"), sendToMenu);
  objectMenu->AddSeparator();
  objectMenu->AddEntry(new OHotString("Cu&t"),   M_EDIT_CUT);
  objectMenu->AddEntry(new OHotString("&Copy"),  M_EDIT_COPY);
  objectMenu->AddEntry(new OHotString("&Paste"), M_EDIT_PASTE);
  objectMenu->AddSeparator();
  objectMenu->AddEntry(new OHotString("Create &Soft Link"), M_FILE_NEWSHORTCUT);
  objectMenu->AddEntry(new OHotString("&Delete"),          M_FILE_DELETE);
  objectMenu->AddEntry(new OHotString("Rena&me"),          M_FILE_RENAME);
  objectMenu->AddSeparator();
  objectMenu->AddEntry(new OHotString("P&roperties"), M_FILE_PROPS);
  objectMenu->SetDefaultEntry(1501);

  sendToMenu->Associate(this);
  objectMenu->Associate(this);

  _newMenu = new OXPopupMenu(_client->GetRoot());
  _newMenu->AddEntry(new OHotString("&Folder"),       M_FILE_NEWFOLDER);
  _newMenu->AddEntry(new OHotString("&Soft Link"),    M_FILE_NEWSHORTCUT);
  _newMenu->AddSeparator();
  _newMenu->AddEntry(new OHotString("Bitmap Image"),  113);
  _newMenu->AddEntry(new OHotString("Text Document"), 113);
  _newMenu->AddEntry(new OHotString("Briefcase"),     113);

  _menuFile = new OXPopupMenu(_client->GetRoot());
  _menuFile->AddEntry(new OHotString("&Open"),     1501);
  _menuFile->AddEntry(new OHotString("&Explore"),  1502);
  _menuFile->AddEntry(new OHotString("&Find..."),  1503);
  _menuFile->AddSeparator();
  _menuFile->AddPopup(new OHotString("Se&nd To"),  sendToMenu);
  _menuFile->AddSeparator();
  _menuFile->AddPopup(new OHotString("Ne&w"), _newMenu);
  _menuFile->AddSeparator();
  _menuFile->AddEntry(new OHotString("Create &Soft Link"), M_FILE_NEWSHORTCUT);
  _menuFile->AddEntry(new OHotString("&Delete"),          M_FILE_DELETE);
  _menuFile->AddEntry(new OHotString("Rena&me"),          M_FILE_RENAME);
  _menuFile->AddEntry(new OHotString("P&roperties"),      M_FILE_PROPS);
  _menuFile->AddSeparator();
  _menuFile->AddEntry(new OHotString("&Close"),           M_FILE_CLOSE);
  _menuFile->SetDefaultEntry(1501);

  _newMenu->DisableEntry(M_FILE_NEWSHORTCUT);
  _menuFile->DisableEntry(M_FILE_NEWSHORTCUT);
  _menuFile->DisableEntry(M_FILE_DELETE);
  _menuFile->DisableEntry(M_FILE_RENAME);
  _menuFile->DisableEntry(M_FILE_PROPS);

  _menuEdit = new OXPopupMenu(_client->GetRoot());
  _menuEdit->AddEntry(new OHotString("&Undo"),             M_EDIT_UNDO);
  _menuEdit->AddSeparator();
  _menuEdit->AddEntry(new OHotString("Cu&t"),              M_EDIT_CUT);
  _menuEdit->AddEntry(new OHotString("&Copy"),             M_EDIT_COPY);
  _menuEdit->AddEntry(new OHotString("&Paste"),            M_EDIT_PASTE);
  _menuEdit->AddEntry(new OHotString("Paste &Soft Link"), M_EDIT_PASTESHORTCUT);
  _menuEdit->AddSeparator();
  _menuEdit->AddEntry(new OHotString("Select &All"),       M_EDIT_SELECTALL);
  _menuEdit->AddEntry(new OHotString("&Invert Selection"), M_EDIT_INVSELECTION);
//_menuEdit->SetDefaultEntry(M_EDIT_INVSELECTION);
  _menuEdit->DisableEntry(M_EDIT_UNDO);
  _menuEdit->DisableEntry(M_EDIT_CUT);
  _menuEdit->DisableEntry(M_EDIT_COPY);
  _menuEdit->DisableEntry(M_EDIT_PASTE);
  _menuEdit->DisableEntry(M_EDIT_PASTESHORTCUT);

  _sortMenu = new OXPopupMenu(_client->GetRoot());
  _sortMenu->AddEntry(new OHotString("By &Name"),      M_VIEW_ARRANGE_BYNAME);
  _sortMenu->AddEntry(new OHotString("By &Type"),      M_VIEW_ARRANGE_BYTYPE);
  _sortMenu->AddEntry(new OHotString("By &Size"),      M_VIEW_ARRANGE_BYSIZE);
  _sortMenu->AddEntry(new OHotString("By &Date"),      M_VIEW_ARRANGE_BYDATE);
  _sortMenu->AddSeparator();
  _sortMenu->AddEntry(new OHotString("&Auto Arrange"), M_VIEW_ARRANGE_AUTO);
  _sortMenu->CheckEntry(M_VIEW_ARRANGE_AUTO);

  _menuView = new OXPopupMenu(_client->GetRoot());
  _menuView->AddEntry(new OHotString("&Toolbar"),      M_VIEW_TOOLBAR);
  _menuView->AddEntry(new OHotString("Status &Bar"),   M_VIEW_STATUS);
  _menuView->AddSeparator();
  _menuView->AddEntry(new OHotString("Lar&ge Icons"),  M_VIEW_LARGEICONS);
  _menuView->AddEntry(new OHotString("S&mall Icons"),  M_VIEW_SMALLICONS);
  _menuView->AddEntry(new OHotString("&List"),         M_VIEW_LIST);
  _menuView->AddEntry(new OHotString("&Details"),      M_VIEW_DETAILS);
  _menuView->AddSeparator();
  _menuView->AddPopup(new OHotString("Arrange &Icons"), _sortMenu);
  _menuView->AddEntry(new OHotString("Lin&e up Icons"), M_VIEW_LINEUP);
  _menuView->AddSeparator();
  _menuView->AddEntry(new OHotString("&Refresh"),      M_VIEW_REFRESH);
  _menuView->AddEntry(new OHotString("&Options..."),   M_VIEW_OPTIONS);
  _menuView->CheckEntry(M_VIEW_TOOLBAR);
  _menuView->CheckEntry(M_VIEW_STATUS);

  if (MainMode == EXPLORER_MODE) {
    _findMenu = new OXPopupMenu(_client->GetRoot());
    _findMenu->AddEntry(new OHotString("&Files or Folders..."), 1001);
    _findMenu->AddEntry(new OHotString("&Computer..."), 1002);

  _menuTools = new OXPopupMenu(_client->GetRoot());
  _menuTools->AddPopup(new OHotString("&Find"), _findMenu);
  _menuTools->AddSeparator();
  _menuTools->AddEntry(new OHotString("&Mount file system..."),  1003);
  _menuTools->AddEntry(new OHotString("&Dismount file system..."),  1004);
  _menuTools->AddSeparator();
  _menuTools->AddEntry(new OHotString("&Go to..."),  1005);
  }

  _menuHelp = new OXPopupMenu(_client->GetRoot());
  _menuHelp->AddEntry(new OHotString("&Contents"),  M_HELP_CONTENTS);
  _menuHelp->AddEntry(new OHotString("&Search..."), M_HELP_SEARCH);
  _menuHelp->AddSeparator();
  _menuHelp->AddEntry(new OHotString("&About"),     M_HELP_ABOUT);

  _menuFile->Associate(this);
  _menuEdit->Associate(this);
  _menuView->Associate(this);
  _menuHelp->Associate(this);
  _sortMenu->Associate(this);

  _menuBar = new OXMenuBar(this, 1, 1, HORIZONTAL_FRAME);
  _menuBar->AddPopup(new OHotString("&File"), _menuFile, _menuBarItemLayout);
  _menuBar->AddPopup(new OHotString("&Edit"), _menuEdit, _menuBarItemLayout);
  _menuBar->AddPopup(new OHotString("&View"), _menuView, _menuBarItemLayout);
  if (MainMode == EXPLORER_MODE)
    _menuBar->AddPopup(new OHotString("&Tools"), _menuTools, _menuBarItemLayout);
  _menuBar->AddPopup(new OHotString("&Help"), _menuHelp, _menuBarItemLayout);

  AddFrame(_menuBar, _menuBarLayout);

  //-------------- tool bar and separator

  _toolBarSep = new OXHorizontal3dLine(this);

  _toolBar = new OXToolBar(this, 60, 20, HORIZONTAL_FRAME);

  _ddlb = new OXFileSystemDDListBox(_toolBar, 1010);

  _toolBar->AddFrame(_ddlb, new OLayoutHints(LHINTS_LEFT | LHINTS_EXPAND_Y,
                                             0, 0, 2, 2));
  _ddlb->Resize(150, _ddlb->GetDefaultHeight());
  _ddlb->Associate(this);

  int spacing = 8;

  //int spacing = 2;
  for (int i=0; tb_data[i].pixmap != NULL; ++i) {
    OXPictureButton *button;

    if (strlen(tb_data[i].pixmap) == 0) {
      spacing = 8;
      continue;
    }
    const OPicture *testPic = _client->GetPicture(tb_data[i].pixmap);
    if (!testPic)
      FatalError("Pixmap not found: %s", tb_data[i].pixmap);
    button = new OXPictureButton(_toolBar, testPic, tb_data[i].id);
    button->SetTip(tb_data[i].tip_text);
    _toolBar->AddFrame(button, new OLayoutHints(LHINTS_TOP | LHINTS_LEFT,
                                                spacing, 0, 2, 2));
    button->Associate(this);
    button->AllowStayDown(tb_data[i].stay_down);
    tb_data[i].button = button;
    spacing = 0;
  }

  AddFrame(_toolBarSep, new OLayoutHints(LHINTS_TOP | LHINTS_EXPAND_X));
  AddFrame(_toolBar, new OLayoutHints(LHINTS_TOP | LHINTS_EXPAND_X));

  //-------------- panes

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

  if (MainMode == EXPLORER_MODE) {
    _v1 = new OXVerticalFrame(_hf, 10, 10, FIXED_WIDTH);

    _treeHdr = new OXCompositeFrame(_v1, 10, 10, SUNKEN_FRAME);
    _listHdr = new OXCompositeFrame(_v2, 10, 10, SUNKEN_FRAME);

    _lbl1 = new OXLabel(_treeHdr, new OString("All Folders"));
    _lbl2 = new OXLabel(_listHdr, new OString("Contents of \".\""));

    _treeHdr->AddFrame(_lbl1, new OLayoutHints(LHINTS_LEFT | LHINTS_CENTER_Y,
                                               3, 0, 0, 0));
    _listHdr->AddFrame(_lbl2, new OLayoutHints(LHINTS_LEFT | LHINTS_CENTER_Y,
                                               3, 0, 0, 0));

    _v1->AddFrame(_treeHdr, new OLayoutHints(LHINTS_TOP | LHINTS_EXPAND_X,
                                             0, 0, 1, 2));
    _v2->AddFrame(_listHdr, new OLayoutHints(LHINTS_TOP | LHINTS_EXPAND_X,
                                             0, 0, 1, 2));

    _v1->Resize(_treeHdr->GetDefaultWidth()+100, _v1->GetDefaultHeight());

    _hf->AddFrame(_v1, new OLayoutHints(LHINTS_LEFT | LHINTS_EXPAND_Y,
                                      0, 2, 0, 0));
  }

  _hf->AddFrame(_v2, new OLayoutHints(LHINTS_RIGHT |
                                      LHINTS_EXPAND_X | LHINTS_EXPAND_Y));

  //-------------- tree

  if (MainMode == EXPLORER_MODE) {
    //_treeView = new OXCanvas(_v1, -1);
    _treeView = new OXCanvas(_v1, 10, 10, SUNKEN_FRAME | DOUBLE_BORDER);

    _lt = new OXListTree(_treeView->GetViewPort(), 10, 10, HORIZONTAL_FRAME,
                                                         _whitePixel);
    _lt->Associate(this);
    _treeView->SetContainer(_lt);
    _v1->AddFrame(_treeView, new OLayoutHints(LHINTS_EXPAND_X | LHINTS_EXPAND_Y));
  }

  //-------------- files

  _listView = new OXListView(_v2, 520, 250);

  fileWindow = new OXFileContainer(_listView->GetViewPort(),
                                   MimeTypeList, "*",
                                   520, 250, HORIZONTAL_FRAME,
                                   _whitePixel);
  fileWindow->Associate(this);

  (_listView->GetViewPort())->SetBackgroundColor(_whitePixel);
  _listView->SetContainer(fileWindow);

  _v2->AddFrame(_listView, new OLayoutHints(LHINTS_EXPAND_X | LHINTS_EXPAND_Y));

  AddFrame(_hf, new OLayoutHints(LHINTS_EXPAND_X | LHINTS_EXPAND_Y,
 		 	                   0, 0, 0/*1*/, 0));

  //-------------- status bar

  _statusBar = new OXStatusBar(this, 60, 10);

  AddFrame(_statusBar, new OLayoutHints(LHINTS_BOTTOM | LHINTS_EXPAND_X,
		 		        0, 0, 3, 0));

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

  SetWindowName(getcwd(tmp, BUFSIZ));
  SetIconName("Explorer");
  SetClassHints("Explorer", "Explorer");

  SetMWMHints(MWM_DECOR_ALL, MWM_FUNC_ALL, MWM_INPUT_MODELESS);

  _waitCursor = XCreateFontCursor(GetDisplay(), XC_watch);

  MapSubwindows();

  // we need to use GetDefaultSize() to initialize the layout algorithm...
  Resize(GetDefaultSize());
}

OXExplorerMainFrame::~OXExplorerMainFrame() {
  SaveIniFile();

  delete _toolBar;
  delete _toolBarSep;
  delete _statusBar;
  delete fileWindow; 
  delete _listView; 
 
  delete _menuBarLayout;
  delete _menuBarItemLayout;
 
  delete _menuFile;
  delete _menuEdit;
  delete _menuView;
  delete _menuHelp;
  delete _newMenu;
  delete _sortMenu;

  delete objectMenu;
  delete sendToMenu;

  delete MimeTypeList;
  delete Recycled;

  delete clientX;
}

void OXExplorerMainFrame::CloseWindow() {
  delete this;
}

void OXExplorerMainFrame::ReadIniFile(char *filename) {
  char line[1024], arg[256];

  strcpy(_rcfilename, filename);
  OIniFile rcfile(filename, INI_READ);

  while(rcfile.GetNext(line)) {
    if (strcasecmp(line, "defaults") == 0) {

      if (rcfile.GetItem("DisplayFullPath", arg))
        if (strncasecmp(arg, "true", 4) == 0) DispFullPath = True;

      if (rcfile.GetItem("NewBrowser", arg))
        if (strncasecmp(arg, "true", 4) == 0) NewBrowser = True;

      if (rcfile.GetItem("ShowToolBar", arg))
        if (strncasecmp(arg, "true", 4) == 0) ShowToolBar = True;

      if (rcfile.GetItem("ShowStatusBar", arg))
        if (strncasecmp(arg, "false", 5) == 0) ShowStatusBar = False;

      if (rcfile.GetItem("ViewMode", arg)) {
        if (strcasecmp(arg, "largeicons") == 0) ViewMode = M_VIEW_LARGEICONS;
        else if (strcasecmp(arg, "smallicons") == 0) ViewMode = M_VIEW_SMALLICONS;
        else if (strcasecmp(arg, "list") == 0) ViewMode = M_VIEW_LIST;
        else if (strcasecmp(arg, "details") == 0) ViewMode = M_VIEW_DETAILS;
      }

      if (rcfile.GetItem("SortOrder", arg)) {
        if (strcasecmp(arg, "byname") == 0) SortMode = M_VIEW_ARRANGE_BYNAME;
        else if (strcasecmp(arg, "bytype") == 0) SortMode = M_VIEW_ARRANGE_BYTYPE;
        else if (strcasecmp(arg, "bydate") == 0) SortMode = M_VIEW_ARRANGE_BYDATE;
        else if (strcasecmp(arg, "bysize") == 0) SortMode = M_VIEW_ARRANGE_BYSIZE;
      }
    }
  }

  //--- init view mode...

  SetViewMode(ViewMode, True);

  //--- init sort mode...

  SetSortMode(SortMode);

  //--- toolbar and status bar...

  if (!ShowToolBar) {
    _menuView->UnCheckEntry(M_VIEW_TOOLBAR);
    HideFrame(_toolBar);
    HideFrame(_toolBarSep);
  }

  if (!ShowStatusBar) {
    _menuView->UnCheckEntry(M_VIEW_STATUS);
    HideFrame(_statusBar);
  }

}

void OXExplorerMainFrame::SaveIniFile() {
  char *arg;
  OIniFile rcfile(_rcfilename, INI_WRITE);

  rcfile.PutNext("defaults");

  rcfile.PutItem("DisplayFullPath", DispFullPath ? "True" : "False");
  rcfile.PutItem("NewBrowser", NewBrowser ? "True" : "False");
  rcfile.PutItem("ShowToolBar", GetState(_toolBar) ? "True" : "False");
  rcfile.PutItem("ShowStatusBar", GetState(_statusBar) ? "True" : "False");

  switch (ViewMode) {
    default:
    case M_VIEW_LARGEICONS: arg = "LargeIcons"; break;
    case M_VIEW_SMALLICONS: arg = "SmallIcons"; break;
    case M_VIEW_LIST:       arg = "List";       break;
    case M_VIEW_DETAILS:    arg = "Details";    break;
  }
  rcfile.PutItem("ViewMode", arg);

  switch(SortMode) {
    default:
    case M_VIEW_ARRANGE_BYNAME: arg = "ByName"; break;
    case M_VIEW_ARRANGE_BYTYPE: arg = "ByType"; break;
    case M_VIEW_ARRANGE_BYSIZE: arg = "BySize"; break;
    case M_VIEW_ARRANGE_BYDATE: arg = "ByDate"; break;
  }
  rcfile.PutItem("SortOrder", arg);
}


//--- Display the number of objects in the file container in status bar

void OXExplorerMainFrame::DisplayTotal(int total, int selected) {
  char tmp[256], *fmt;

  if (selected)
    fmt = "%d Object%s, %d selected.";
  else
    fmt = "%d Object%s.";

  sprintf(tmp, fmt, total, (total == 1) ? "" : "s", selected);
  _statusBar->SetText(new OString(tmp));
}


//--- Set file window's view mode and update menu and toolbar buttons
//    accordingly.

void OXExplorerMainFrame::SetViewMode(int new_mode, int force) {
  int i, lv, bnum;

  if (force || (ViewMode != new_mode)) {

    switch (new_mode) {
      default: if (!force) return; else new_mode = M_VIEW_LARGEICONS;
      case M_VIEW_LARGEICONS: bnum = 14; lv = LV_LARGE_ICONS; break;
      case M_VIEW_SMALLICONS: bnum = 15; lv = LV_SMALL_ICONS; break;
      case M_VIEW_LIST:       bnum = 16; lv = LV_LIST;        break;
      case M_VIEW_DETAILS:    bnum = 17; lv = LV_DETAILS;     break;
    }

    ViewMode = new_mode;
    _menuView->RCheckEntry(ViewMode, M_VIEW_LARGEICONS, M_VIEW_DETAILS);

    for (i=14; i<=17; ++i)
      tb_data[i].button->SetState((i == bnum) ? BUTTON_ENGAGED : BUTTON_UP);

    _listView->SetViewMode(lv);
  }
}


//--- Set file sort mode and update menu accordingly.

void OXExplorerMainFrame::SetSortMode(int new_mode) {
  int smode;

  switch (new_mode) {
    default: new_mode = M_VIEW_ARRANGE_BYNAME;
    case M_VIEW_ARRANGE_BYNAME: smode = SORT_BY_NAME; break;
    case M_VIEW_ARRANGE_BYTYPE: smode = SORT_BY_TYPE; break;
    case M_VIEW_ARRANGE_BYDATE: smode = SORT_BY_DATE; break;
    case M_VIEW_ARRANGE_BYSIZE: smode = SORT_BY_SIZE; break;
  }

  SortMode = new_mode;
  _sortMenu->RCheckEntry(SortMode, M_VIEW_ARRANGE_BYNAME, M_VIEW_ARRANGE_BYDATE);
  fileWindow->Sort(smode);
}


void OXExplorerMainFrame::SetTitle() {
  char *p = _currentDir;
     
  if (!DispFullPath) {
    p = strrchr(_currentDir, '/');
    if (p) p = *(++p) ? p : _currentDir;
  }

  SetWindowName(p);
}


void OXExplorerMainFrame::UpdateListBox() {
  char path[PATH_MAX];

  // Use what getcwd returns because the name of the directory
  // may have changed.

  _ddlb->UpdateContents(getcwd(path, PATH_MAX));

  strcpy(_currentDir, path);
  SetTitle();
}

void OXExplorerMainFrame::UpdateTree() {
  if (MainMode == EXPLORER_MODE) {
    OListTreeItem *root;
    char *dir = "/";

    root = _lt->AddItem(NULL, dir, _client->GetPicture("fdisk.t.xpm"),
                                   _client->GetPicture("fdisk.t.xpm"));
    root->open = True;
    XDefineCursor(GetDisplay(), _id, _waitCursor);
    XFlush(GetDisplay());
    ReadDir(dir, root);
    _lt->SortChildren(NULL);
    _client->NeedRedraw(_lt);
    XDefineCursor(GetDisplay(), _id, None);
  }
}

void OXExplorerMainFrame::ReadDir(char *cdir, OListTreeItem *parent) {
  DIR *dirp;
  struct stat sbuf;
  struct dirent *dp;
  char *name;
  char filename[256], tmp[PATH_MAX];
  OListTreeItem *current;
  const OPicture *pic1 = NULL, *pic2 = NULL;

  getcwd(tmp, PATH_MAX);

  //_lt->DeleteChildren(parent);

  if (chdir(cdir) != 0) return;

  //printf("entering %s...\n", cdir);

  if ((dirp = opendir(".")) == NULL) {
    chdir(tmp);
    return;
  }

  while ((dp = readdir(dirp)) != NULL) {
    name = dp->d_name;
    if (strcmp (name, ".") && strcmp (name, "..")) {
      if (lstat(name, &sbuf) == 0) {
        if (S_ISDIR(sbuf.st_mode)) {
          strcpy(filename, name);
          current = _lt->FindChildByName(parent, filename);
          if (!current) {
            if (strcmp(name, ".desktop") == 0) {
              pic1 = _client->GetPicture("desktop.t.xpm");
              pic2 = _client->GetPicture("desktop.t.xpm");
            } else if (strcmp(name, ".xclass.recycle") == 0) {
              pic1 = _client->GetPicture("recycle-empty.t.xpm");
              pic2 = _client->GetPicture("recycle-empty.t.xpm");
            } else {
              pic1 = NULL;
              pic2 = NULL;
            }
            current = _lt->AddItem(parent, filename, pic1, pic2);
          }
          ReadSubDirs(filename, current);
          _lt->SortChildren(current);
        }
      } else {
        fprintf(stderr, "lstat: \"%s\": ", name);
        perror("");
      }
    }
  }

  _lt->SortChildren(parent);
  closedir(dirp);
  chdir(tmp);
}

void OXExplorerMainFrame::ReadSubDirs(char *cdir, OListTreeItem *parent) {
  DIR *dirp;
  struct stat sbuf;
  struct dirent *dp;
  char *name;
  char filename[256], tmp[PATH_MAX];
  OListTreeItem *current;

  getcwd(tmp, PATH_MAX);

  if (chdir(cdir) != 0) return;

  //printf("entering %s...\n", cdir);
  _lt->DeleteChildren(parent);

  if ((dirp = opendir(".")) == NULL) {
    chdir(tmp);
    return;
  }

  while ((dp = readdir(dirp)) != NULL) {
    name = dp->d_name;
    if (strcmp (name, ".") && strcmp (name, "..")) {
      if (lstat(name, &sbuf) == 0) {
        if (S_ISDIR(sbuf.st_mode)) {
          strcpy(filename, name);
          current = _lt->AddItem(parent, filename);
          // break;
        }
      } else {
        fprintf(stderr, "lstat: \"%s\" -- ", name);
        perror("stat");
      }
    }
  }

  closedir(dirp);
  chdir(tmp);
}


int OXExplorerMainFrame::ProcessMessage(long msg, long parm1, long parm2) {

  switch (GET_MSG(msg)) {

    case XC_COMMAND:    // buttons and menu commands
      switch(GET_SUBMSG(msg)) {

        // process menu and buttons together
        case CM_BUTTON:
        case CM_MENU:
          switch(parm1) {

            case 1501:
              {
              OFileInfo fi;

              fi.MimeTypesList = MimeTypeList;
              fi.file_types = NULL;
              new OXFileDialog(_client->GetRoot(), this, FDLG_OPEN, &fi);
              }
              break;

            case M_FILE_PROPS:
              if (fileWindow->NumSelected() == 1) {
                const OXFileItem *f;
                void *iterator = NULL;

                f = (OXFileItem *) fileWindow->GetNextSelected(&iterator);
                new OXPropertiesDialog(clientX->GetRoot(), this,
                                       new OString(f->GetName()),
                                       f->GetType(),
                                       f->IsSymLink());
              } else {
                // group file properties: chmod only?
                XBell(GetDisplay(), 0);
              }
              break;

            case M_FILE_DELETE:
              DeleteFiles();
              break;

            case M_FILE_CLOSE:
              delete this; // mainWindow;
              delete clientX;
              exit(0);
              break;

            case M_EDIT_SELECTALL:
              fileWindow->SelectAll();
              break;

            case M_EDIT_INVSELECTION:
              fileWindow->InvertSelection();
              break;

            case M_VIEW_TOOLBAR:
              if (GetState(_toolBar)) {
                HideFrame(_toolBar);
                HideFrame(_toolBarSep);
                _menuView->UnCheckEntry(M_VIEW_TOOLBAR);
              } else {
                ShowFrame(_toolBarSep);
                ShowFrame(_toolBar);
                _menuView->CheckEntry(M_VIEW_TOOLBAR);
              }
              break;

            case M_VIEW_STATUS:
              if (GetState(_statusBar)) {
                HideFrame(_statusBar);
                _menuView->UnCheckEntry(M_VIEW_STATUS);
              } else {
                ShowFrame(_statusBar);
                _menuView->CheckEntry(M_VIEW_STATUS);
              }
              break;

            case M_VIEW_OPTIONS:
              new OXOptionsDialog(clientX->GetRoot(), this);
              break;

            case M_VIEW_LARGEICONS:
            case M_VIEW_SMALLICONS:
            case M_VIEW_LIST:
            case M_VIEW_DETAILS:
              SetViewMode(parm1);
              break;

            case M_VIEW_ARRANGE_BYNAME:
            case M_VIEW_ARRANGE_BYTYPE:
            case M_VIEW_ARRANGE_BYSIZE:
            case M_VIEW_ARRANGE_BYDATE:
              SetSortMode(parm1);
              break;

            case M_VIEW_REFRESH:
              fileWindow->DisplayDirectory();
              UpdateListBox();
              break;

            case 99:
              fileWindow->ChangeDirectory("..");
              UpdateListBox();
              break;

            default:
              XBell(GetDisplay(), 0);
              break;

          } // switch(parm1)
          break;

        case CM_DDLISTBOX:
          if (parm1 == 1010) {
            OXTreeLBEntry *e = (OXTreeLBEntry *) _ddlb->GetSelectedEntry();
            if (e) {
              fileWindow->ChangeDirectory(e->GetPath()->GetString());
              UpdateListBox();
            }
          }
          break;

        default:
          break;

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

    case XC_CONTAINER:
      switch(GET_SUBMSG(msg)) {
        case CT_ITEMCLICK:
          if (parm1 == Button3) {
            int x_root = parm2 & 0xFFFF;
            int y_root = (parm2 >> 16) & 0xFFFF;
            objectMenu->PlaceMenu(x_root, y_root, False, True);
          }
          break;

        case CT_ITEMDBLCLICK:
          if (parm1 == Button1) {
            int sel;
            void *iterator = NULL;
            OXFileItem *f;

            if ((sel = fileWindow->NumSelected()) == 1) {
              f = (OXFileItem *) fileWindow->GetNextSelected(&iterator);
              DoAction(f);
            }
          }
          break;

        case CT_SELCHANGED:
          DisplayTotal(parm1, parm2);
          break;

        default:
          break;
      }
      break;

//    case FW_DIRCHANGED:
//      UpdateListBox();
//      break;

    case XC_LISTTREE:
      switch(GET_SUBMSG(msg)) {
        case CT_ITEMDBLCLICK:
          { OListTreeItem *h;
            if ((h = _lt->GetSelected()) != NULL) {
              char *p, tmp[2048], hdr[2048];
              _lt->GetPathnameFromItem(h, tmp);
              p = tmp; 
              while (*p && *(p+1) == '/') ++p;
              sprintf(hdr, "Contents of \"%s\"", p);
              _lbl2->SetText(new OString(hdr));
              _listHdr->Layout();
              XDefineCursor(GetDisplay(), _id, _waitCursor);
              XFlush(GetDisplay());
              if (strcmp(p, _currentDir) != 0) {
                fileWindow->ChangeDirectory(p);
                UpdateListBox();
              }
              XDefineCursor(GetDisplay(), _id, None);
            }
          }
          break;

        case CT_ITEMCLICK:
          { OListTreeItem *h;
            if ((h = _lt->GetSelected()) != NULL) {
              if (h->open) {
                char *p, tmp[2048], hdr[2048];
                _lt->GetPathnameFromItem(h, tmp);
                p = tmp; 
                while (*p && *(p+1) == '/') ++p;
                // sprintf(hdr, "Contents of \"%s\"", p);
                // _lbl2->SetText(new OString(hdr));
                // _listHdr->Layout();
                XDefineCursor(GetDisplay(), _id, _waitCursor);
                XFlush(GetDisplay());
                ReadDir(p, h);
                // if (strcmp(p, _currentDir) != 0) {
                  // fileWindow->ChangeDirectory(p);
                  // UpdateListBox();
                // }
                XDefineCursor(GetDisplay(), _id, None);
              }
              _client->NeedRedraw(_lt);
            }
          }
          break;
      }
      break;

    default:
      break;

  } // switch(GET_MSG(msg))

  return True;
}

void OXExplorerMainFrame::DoAction(OXFileItem *f) {
  int   pid, ftype;
  char  action[PATH_MAX];
  char  fullaction[PATH_MAX];
  char  command[PATH_MAX];
  char  filename[PATH_MAX];
  char  path[PATH_MAX]; 
  char *argv[PATH_MAX]; 
  int   argc = 0;
  char *argptr;

  // get file type
  ftype = f->GetType();

  // is a directory?
  if (S_ISDIR(ftype)) { 
    if (NewBrowser) {
      pid = fork();
      if (pid == 0) {
        execlp(AppPath,
               AppPath,
               f->GetName()->GetString(),
               NULL);
        // if we are here then execlp failed!
        fprintf(stderr, "Cannot spawn \"%s\": execlp failed!\n", AppPath);
        exit(1);
      }
    } else {
      fileWindow->ChangeDirectory(f->GetName()->GetString());
//      SendMessage(_msgWindow, MK_MSG(FW_DIRCHANGED, 0), 0, 0);
      UpdateListBox();
    }

  // execute the action specified in MimeTypeList, if any.
  } else if (MimeTypeList->GetAction(f->GetName()->GetString(), action)) {
    getcwd(path, PATH_MAX);
    sprintf(filename, "%s/%s", path, f->GetName()->GetString());
    argptr = strtok(action, " ");
    while (argptr != NULL) {
      if (strcmp(argptr, "%s") == 0) {
        argv[argc] = new char[strlen(filename)+1];
        strcpy(argv[argc], filename);
        argc++;  
      } else {
        argv[argc] = new char[strlen(argptr)+1];
        strcpy(argv[argc], argptr);
        argc++;   
      }
      argptr = strtok(NULL, " ");
    }
    argv[argc] = NULL;
    pid = fork();
    if (pid == 0) {
      execvp(argv[0], argv);
      // if we are here then execlp failed!
      fprintf(stderr, "Cannot spawn \"%s\": execvp failed!\n", argv[0]);
      exit(1);
    }
    for (int i = 0; i < argc; i++) delete[] argv[i];

  // is an exec file?
  } else if (S_ISREG(ftype) && (ftype & S_IXUSR)) {
    pid = fork();
    if (pid == 0) {  
      getcwd(path, PATH_MAX);
      sprintf(filename, "%s/%s", path, f->GetName()->GetString());
      execlp(filename, filename, NULL, NULL);
      // if we are here then execlp failed!
      fprintf(stderr, "\"%s\" failed to run!\n", f->GetName()->GetString());
      exit(1);
    }
  }
}

void OXExplorerMainFrame::DeleteFiles() {
  OXFileItem   *f;  
  void         *iterator = NULL;
  struct stat   inode;
  const  char  *basename;
  char          dirname[PATH_MAX];
  char          newfilename[PATH_MAX];
  char          filename[PATH_MAX];
  char          recycle[PATH_MAX];
  char          recfilename[PATH_MAX];
  int           index = 0;
  time_t t;
  int    sel, retval;
  const  OPicture *pic;
  char  *title, prompt[256];

  if ((sel = fileWindow->NumSelected()) == 0) return;

  f = (OXFileItem *) fileWindow->GetNextSelected(&iterator);

  if (sel == 1) {
    pic = _client->GetPicture("srecycle.xpm");
    sprintf(prompt, "Are you sure you want to send \"%s\" to the Recycle Bin?",
            f->GetName()->GetString());
    title = "Confirm File Delete";
  } else {
    pic = _client->GetPicture("mrecycle.xpm");
    sprintf(prompt, "Are you sure you want to send these %d items to the Recycle Bin?",
            sel);
    title = "Confirm Multiple File Delete";
  }

  new OXMsgBox(clientX->GetRoot(), this,
               new OString(title), new OString(prompt), pic,
               ID_YES | ID_NO, &retval);

  if (retval != ID_YES) return;

  getcwd(dirname, PATH_MAX);
  strcpy(recycle, Recycled->GetRecycleBin());

  while (f) {
    basename = f->GetName()->GetString();
    if ((strcmp(basename, ".") != 0) && (strcmp(basename, "..") != 0)) {
      sprintf(filename, "%s/%s", dirname, basename);
      sprintf(recfilename, "%s/%s", recycle, basename);
      sprintf(newfilename, "%s", basename);
      index = 0;
      while (stat(recfilename, &inode) == 0) {
        sprintf(newfilename, "%s.%d", basename, index++);
        sprintf(recfilename, "%s/%s", recycle, newfilename);
      }  
      time(&t);
      Recycled->AddFile(basename, newfilename,
                        ctime((const time_t *) &t), filename);
      if (rename(filename, recfilename) != 0) {
        sprintf(prompt, "Cannot delete file \"%s\": %s.",
                basename, strerror(errno));
        new OXMsgBox(_client->GetRoot(), this,
                     new OString("Error"), new OString(prompt),
                     MB_ICONSTOP, ID_OK);
        
      }
    }
    f = (OXFileItem *) fileWindow->GetNextSelected(&iterator);
  }

  fileWindow->DisplayDirectory();
}
