OZ++ Class: FTFileListView
/******************************************************************************
 *
 * Copyright (c) 2015   TOSHIYUKI ARAI. ALL RIGHTS RESERVED.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions, and the following disclaimer.
 *
 * 2. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *
 *  FTFileListView.h
 *
 *****************************************************************************/


#include <oz++/motif/ScrolledWindow.h>
#include <oz++/motif/BulletinBoard.h>
#include <oz++/motif/Color.h>
#include <oz++/motif/PixmapFile.h>
#include <oz++/xft/FTFont.h>
#include <oz++/xft/FTColumnLayout.h>
#include <oz++/xft/FTMultiColumn.h>
#include <oz++/SmartArray.h>
#include <oz++/FileAttributes.h>
#include <oz++/DirectoryScanner.h>
#include <oz++/MonetaryFormatter.h>
#include <oz++/FileAttributes.h>
#include <oz++/LocalTime.h>
#include <oz++/SmartPtrs.h>

namespace OZ {
const char* XmNsizeUnitKB       = "sizeUnitKB";
const char* XmNfolderIconFile   = "folderIconFile";
const char* XmNfileIconFile     = "fileIconFile";
  
class FTFileListView: public ScrolledWindow {
public:
  typedef enum {
   DIR  = 1,
   FILE = 2,
  } FILE_TYPE;

private:  
  FTFont*  font;
  PixmapFile* folderIcon;
  PixmapFile* fileIcon;
  
  BulletinBoard* bboard;
  FTColumnLayout* columnLayout;
  int numColumns;

  SmartArray<FTMultiColumn*> columns;
  int index;
  int columnHeight;
  int columnWidth;
  
  bool fileSizeUnitKB;
private:
  static const int MARGIN = 2;
  
public:
  FTFileListView(View* parent, const char* name, Args& args)
  :ScrolledWindow(parent, name, args.set(XmNscrollingPolicy, XmAUTOMATIC)
                                      .set(XmNscrollBarDisplayPolicy, XmVARIABLE) ),
  font(NULL),
  folderIcon(NULL),
  fileIcon(NULL),
  
  bboard(NULL),
  columnLayout(NULL),
  numColumns(0),
  index(0),
  columnHeight(0),
  columnWidth(0),
  fileSizeUnitKB(true)
  {
    Color color(this);
    Pixel white = color.whitePixel();
    int fontsize = 12;
    unsigned int iconw = 0;
    unsigned int iconh = 0;
    
    if (args.has(XmNfolderIconFile)) {
      const char* filename = (const char*)args.get(XmNfolderIconFile);
      folderIcon = new PixmapFile(getDisplay(), filename, white, white);
      if (folderIcon) {
        folderIcon -> getGeometry(&iconw, &iconh);  
      }
    }
    if (args.has(XmNfileIconFile)) {
      const char* filename = (const char*)args.get(XmNfileIconFile);
      fileIcon = new PixmapFile(getDisplay(), filename, white, white);
    }
    
    Args ar;
    ar.reset();
    if (args.has(XftNfamily)) {
      ar.set(XftNfamily, (const char*)args.get(XftNfamily));
    } else {
      ar.set(XftNfamily, "Times-12");  //Default
    }
    if (args.has(XftNweight)) {
      ar.set(XftNweight, (int)args.get(XftNweight));  
    } else {
      ar.set(XftNweight, FC_WEIGHT_THIN);//;
    }
    if (args.has(XftNsize)) {
      fontsize = (int)args.get(XftNsize); //
      ar.set(XftNsize, fontsize);
    } else {
      ar.set(XftNsize, fontsize);  //Default
    }
    font = new FTFont(getDisplay(), ar);
  
    if (iconh > fontsize) {
      columnHeight = iconh + MARGIN;
    } else {
      columnHeight = fontsize *2; //
    }
    
    ar.reset();
    ar.set(XmNmarginWidth, 0);
  ar.set(XmNmarginHeight, 0);
  ar.set(XmNbackground, white);
    bboard = new BulletinBoard(this, "", ar);
  if (args.has(XmNcolumnLayout) && args.has(XmNnumColumns) ) {
    columnLayout = (FTColumnLayout*)args.get(XmNcolumnLayout);
    numColumns   = (int)args.get(XmNnumColumns);
  } else {
    throw IException("XmNcolumnLayout or XmNnumColumns is not sepecified");  
  }
  
    columnWidth = MARGIN*2;
  for (int i = 0; i<numColumns; i++) {
    columnWidth += columnLayout[i].width;
    }
    

  } 
  
  ~FTFileListView()
  {
    columns.clear();
    delete font;
    delete folderIcon;
    delete fileIcon;
  delete bboard;
    
  }
  
  void removeAllItems()
  {
     columns.removeAllItems();
     index = 0;
  }

  void addItems(const char* dir, int m, char** names, int type, PixmapFile* icon)
  {
    char fullpath[PATH_MAX];
     
    for (int i = 0; i<m; i++) {
      try {
       sprintf(fullpath, "%s/%s", dir, names[i]);
       FileAttributes attr(fullpath);

       Args ar;
        ar.reset();
       ar.set(XmNx, MARGIN);
       ar.set(XmNy, (columnHeight+ MARGIN*2) * index + MARGIN);
        ar.set(XmNrecomputeSize, False);
        ar.set(XmNcolumnLayout, (XtArgVal)columnLayout);
        ar.set(XmNnumColumns, numColumns);
       ar.set(XmNwidth, columnWidth);
        ar.set(XftNfont, (XtArgVal)font);
        //ar.set(XmNhighlightOnEnter, True);
        ar.set(XmNuserData, type);
        CompoundString cs("");
        ar.set(XmNlabelString, cs);
        ar.set(XmNlabelType, XmPIXMAP_AND_STRING);
        if (icon) {
          ar.set(XmNlabelType, XmPIXMAP_AND_STRING);
          ar.set(XmNlabelPixmap, (XtArgVal)icon->get());
        }
        
        FTMultiColumn* col = new FTMultiColumn(bboard, "", ar);
        
       LocalTime lt(attr.changedTime());
        CharString ctime = lt.toCharString();
         
        char csize[128];
        MonetaryFormatter mformatter;  
        if (fileSizeUnitKB) {
          unsigned long fs = attr.getSize();
          unsigned long kb = fs/1024L;
          if ((fs % 1024L)) {
            kb++;
          }
          CharString value = mformatter.toString(kb);
          sprintf(csize, "%s KB", (const char*)value);
        } else {
          CharString value = mformatter.toString((long)attr.getSize());
          sprintf(csize, "%s B", (const char*)value);
        }

       const char* items[(const int)numColumns];
       int n = 0;
       items[n++] = names[i];
         items[n++] = (const char*)ctime;
       items[n++] = csize;
       col -> setItems(items, n);
        
        
        columns[index++] = col;
      } catch (Exception& ex) {
         ex.display();  
      }
    }
  }
  
  void addEventHandler(unsigned long eventMask, CommonObject* object, Handler handler, XtPointer unused)
  {
    for (int i = 0; i<index; i++) {
      FTMultiColumn* col = columns[i];
      if (col) {
      //  col -> resize();
        col -> addEventHandler(eventMask, object,
          handler, to_voidptr(i));
      }
    }
  }
  
  void  listupDirectories(const char* dir, const char* pattern)
  {
    DirectoryScanner scanner(dir);
    DirEntryList dirList;
    int m = scanner.scanDir(pattern, dirList);
    if (m > 0) {
      SmartPtrs<char*> names = new char*[m];
      dirList.getNames((char**)names);
      
      addItems(dir, m, names, DIR, folderIcon); 
    }
  }

  void  listupFiles(const char* dir, const char* pattern)
  {
    DirectoryScanner scanner(dir);
    DirEntryList dirList;
    int m = scanner.scanFile(pattern, dirList);
    if (m > 0) {
      SmartPtrs<char*> names = new char*[m];
      dirList.getNames((char**)names);
      addItems(dir, m, names, FILE, fileIcon);
      
    }
  }
};

}