OZ++ Class: FTMultiColumn
/******************************************************************************
 *
 * 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.
 *
 *
 *  FTMultiColumn.h
 *
 *****************************************************************************/

#pragma once

#include <oz++/motif/ApplicationView.h>
#include <oz++/motif/DrawnButton.h>
#include <oz++/motif/PixmapFile.h>
#include <oz++/SmartArray.h>
#include <oz++/CharString.h>

#include <oz++/xft/FTPrimitive.h>
#include <oz++/xft/FTDC.h>
#include <oz++/xft/FTColumnLayout.h>



namespace OZ {
  
/*
typedef struct {
  int   width;
  int   alignment;
  const char* color;
} FTColumnLayout;
*/
  
class FTMultiColumn :public FTPrimitive {
private:  
  FTColumnLayout*  columns;
  SmartArray<CharString*> labels;
  
  int          numColumns;
  XRectangle*  rectangles;
  int itemNum;
  
private:
  static const int MARGIN = 4;
  
public:
  FTMultiColumn(View* parent, const char* name, Args& args)
  :FTPrimitive(parent, name, args.set(XmNpushButtonEnabled, False)
                                 .set(XmNalignment, XmALIGNMENT_BEGINNING )
                                 .set(XmNhighlightColor, parent->color("blue"))
                                 .set(XmNhighlightThickness, 1)
                                 .set(XmNbackground, parent->whitePixel())
                                 .set(XmNshadowThickness, 0)
                                 .set(XmNtraversalOn, True) ),
  columns(NULL),
  numColumns(0),
  rectangles(NULL),
  itemNum(0)
  {
    if (args.has(XmNcolumnLayout) && args.has(XmNnumColumns) ) {
      //Shallow copy
      columns = (FTColumnLayout*) args.get(XmNcolumnLayout);
      numColumns = (int) args.get(XmNnumColumns);
    } else {
      throw IException("Invalid argument");  
    }
    
    short x = 0;
    short y = 0;
    
    rectangles = new XRectangle[numColumns];
    
    for (int i = 0; i<numColumns; i++) {
      unsigned short w = (unsigned short)columns[i].width;
      XRectangle* r = &rectangles[i];
      r -> x = x;
      r -> y = y;
      r -> width = w;// + MARGIN*2;
      r -> height = 30;
     //printf("%d %d %d %d\n", x, y, w, h);
      x += w;
    }
  }

  ~FTMultiColumn()
  {
    columns = NULL;
 
    delete [] rectangles;
    rectangles = NULL;
  }

  void setItems(const char** items, int num)
  {
    if (items && num >0 && num <=numColumns) {
      labels.removeAllItems();
      itemNum = num;
      for (int i = 0; i<num; i++) {
        //printf("%s \n", items[i]);
        labels[i] = new CharString(items[i]);
      }
    } else {
      printf("Invalid argument\n");  
    }
  }
  
  const char* getItem(int pos)
  {
    const char* item = NULL;
  
    if (pos >=0 && pos < itemNum) {
      CharString* cstring = labels[pos];
      if (cstring) {
        item = (const char*)(*cstring);
      }
    } else {
      printf("Invalid argument %d\n", pos);  
    }
    return item;
  }

  void expose(Action& action)
  {
    Display* display = getDisplay();
    FTDC dc(this -> getWidget());
    FTFont* font = getFont();
    if (font == NULL) {
      printf("Font is NULL");
      return;  
    }
    if (labels.getSize() == 0) {
      return;  
    }
    dc.setFont(font);
    int th = 0;    
    //dc.setClipRectangles(0, 0, rectangles, numColumns);
    int height = 0;
    for (int i = 0; i < numColumns; i++) {
      CharString* cstring = NULL;
      try {
        cstring = labels[i];
      } catch (Exception& ex) {
        ex.display();
      }
      if (cstring == NULL) {
      break;
      }
      const char* string = (const char*)(*cstring);
      //printf("%d %s \n", i, string);
      XGlyphInfo ex;    
      font -> textExtentsUtf8(string, strlen(string), &ex);
      int asc = ex.y;
      int des = ex.height - ex.y;
      int x = rectangles[i].x + MARGIN;
      int y = rectangles[i].y;
    
      int width = rectangles[i].width;
      int alignment = columns[i].alignment;
 
      if (alignment == XmALIGNMENT_BEGINNING) {
        ;//Do nothing here
      } 
      if (alignment == XmALIGNMENT_CENTER) { 
         x = x + (width - ex.width)/2;
      }
      if (alignment == XmALIGNMENT_END) {
        x = x + width  - ex.width - MARGIN - height;  
      }
   
     if (i == 0) {
       get(XmNheight, (XtArgVal)&height);
        //set(XmNheight, ex.height + MARGIN*2);
        //height = ex.height + MARGIN*2;
      }
      XRectangle* r = &rectangles[i];
      //r -> height = ex.height + MARGIN*2;
      r -> height = height;// + MARGIN*2;

      y = y + (height - ex.height)/2 + ex.height - des/2;
      dc.setClipRectangles(height, 0, &rectangles[i], 1);
      dc.drawStringUtf8(x+height, y, string, strlen(string), columns[i].color );
     // printf("%d %d %s\n", x, y, columns[i].label);
      //y += MARGIN*2;
    }
  }
};

}