SOL9 2.0 Class: Composite

 SOL9 C++ Class Library  SOL9 Samples  SOL9 Tutorial  SOL9 FAQ  SOL9 ClassTree  SOL9 ClassList 

Source code

/******************************************************************************
 *
 * Copyright (c) 1999-2012 Antillia.com 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.
 *
 *
 *  Composite.h
 *
 *****************************************************************************/

// SOL++2000
// 1999.10.09 Modified layout to be virtual.
// 1999.10.09 Added a getLayoutManager method.
// 2000.02.18
// 2001.03.11 Added a create method.
// SOL9
// 2009/09/07 Added setIcon method to sepecifiy an Icon Id in resource file(*.rc)
// 2012/07/15 Modified doHorizScroll and doVertScroll to be virtual functions.

#pragma once

#include <sol\View.h>
#include <sol\Extent.h>
#include <sol\LayoutManager.h>
#include <sol\Primitive.h>
#include <sol\LayoutManager.h>
#include <sol\Font.h>
#include <sol\Application.h>
#include <sol\ScrollBar.h>
#include <shellapi.h>  
#include <sol\stdio.h>
#include <sol\Button.h>
#include <sol\ListBox.h>
#include <sol\ComboBox.h>
#include <sol\Text.h>
#include <sol\ClientDC.h>
#include <sol\stdio.h>

namespace SOL {

#define Max(a,b)    (((a) > (b)) ? (a) : (b))
#define Min(a,b)    (((a) < (b)) ? (a) : (b))


class Composite :public View {

private:
  static int stylesCount()
  {
    int count = 0;
    styles(&count);

    return count;
  }

  static Arg* styles(int* count=NULL)
  {
    static Arg _styles[] = {
      {XmNhorizScrollBar, WS_HSCROLL},
      {XmNvertScrollBar,  WS_VSCROLL},
    };

    if (count) {
      *count = XtNumber(_styles);
    }
    return _styles;
  }

  HICON   hicon;
  HWND  hfocus;

  Extent  extent;
  int    pageIncrement;
  int    lineIncrement;
  Boolean  enableThumbTrack;
  LayoutManager* layoutManager;
  
  Font  defaultFont;

private:
  //2008/06/23
  void setDefaultFont()
  {
    LOGFONT lf;
    getDisplayFont(lf);

    defaultFont.create(&lf);

    View::setFont(&defaultFont);
  }


public:
  static const int HORIZONTAL = (int)SB_HORZ;
  static const int VERTICAL   = (int)SB_VERT;

public:
  Composite():View() {
    hicon    = NULL;
    hfocus    = NULL;
    pageIncrement = 16;
    lineIncrement = 2;
    enableThumbTrack =False;
    layoutManager = NULL;
  }


public:
  int Composite::moveThumb(Event& event, int type)
  {
    int pos  = getScrollPos(type);
    int prev = pos;

    SCROLLINFO scInfo;
    //<modified date="2000.05.08">
    memset(&scInfo, 0, sizeof(scInfo));
    scInfo.cbSize = sizeof(scInfo);
    //</modified>
    scInfo.fMask = SIF_ALL;

    int min, max;
    getScrollRange(type, &min, &max);
    int range = max - min;

    int request = LOWORD(event.getWParam());

    switch(request) {

    case SB_PAGEDOWN: pos += pageIncrement;
    case SB_LINEDOWN: pos = Min(range, pos + lineIncrement);
      break;

    case SB_PAGEUP:  pos -= pageIncrement;
    case SB_LINEUP:  pos = Max(0, pos - lineIncrement);
      break;

    case SB_TOP: pos = 0;
      break;

    case SB_BOTTOM:  pos = range;
      break;

    case SB_THUMBPOSITION:
      getScrollInfo(type, &scInfo);
      pos = scInfo.nTrackPos;
      break;

    case SB_THUMBTRACK:
      if(enableThumbTrack) {
        getScrollInfo(type, &scInfo);
        pos = scInfo.nTrackPos;
      }
      else {
        return 0;
      }
      break;

    default:
      break;
      }
    setScrollPos(type, pos);
    return  prev - pos;
  }
  

public:
  //2012/07/15
  virtual void doHorizScroll(Action& action)
  {
    int diff = action.getIntValue();
    scroll(diff, 0, NULL, NULL);
    update();
  }

public:
  //2012/07/15
  virtual void doVertScroll(Action& action)
  {
    int diff = (int)action.getIntValue();
    scroll(0, diff, NULL, NULL);
    update();
  }


protected:
  virtual long horizScroll(Event& event)
  {
    HWND hwnd = (HWND)event.getLParam();

    int  diff = 0;

    if(hwnd) { // scrollbar control.
      ScrollBar* scb = (ScrollBar*)Application::lookup(hwnd);
      if(scb)  diff = scb -> moveThumb(event);
    }
    else {     // standard scrollbar
      hwnd = getWindow();
      diff = moveThumb(event, SB_HORZ);
    }
    callCallback(XmNhorizScrollCallback, (Key)hwnd, (void*)diff, event);
    return NULL;
  }

protected:
  virtual long vertScroll(Event& event)
  {
    HWND hwnd = (HWND)event.getLParam();

    int  diff = 0;

    if(hwnd) {// scrollbar control.
      ScrollBar* scb = (ScrollBar*)Application::lookup(hwnd);
      if(scb)  diff = scb -> moveThumb(event);
    }
    else {    // standard scrollbar
      hwnd = getWindow();
      diff = moveThumb(event, SB_VERT);
    }
    callCallback(XmNvertScrollCallback, (Key)hwnd, (void*)diff, event);
    return NULL;
  }


protected:
  virtual long command(Event& event) 
  {
    WORD menuId = (WORD)event.getMenuId();
    HWND child  = event.getChild();

    //  Printf("Composite::Command \r\n");
    if(event.isMenuSelected() == TRUE ||
      event.fromAccelerator() == TRUE) {
      // menu or accelarator has been selected. 
      callCallback(XmNmenuCallback, (Key)menuId,
        NULL,event);
    }
    else {
      //2009/11/01
      if (child && IsWindow(child)) {
        View* view = (View*)Application::lookup(child);
        const TCHAR* name = XmNactivateCallback;
        if(view) {
          name = view->getCallbackName(event);
        }
      //  Printf("Composite::Command %s\r\n", name);
        callCallback(name, (Key)child, NULL, event);
      }
    }

    // Return a result of callback.
    return event.getResult();
  }

protected:
  // Common notification from common controls.
  const TCHAR* isCommonNotification(Event& event)
  {
    static Arg table[] = {
    {XmNerrorSpaceCallback, (LONG_PTR)NM_OUTOFMEMORY},
    {XmNclickCallback,      (LONG_PTR)NM_CLICK},
    {XmNdoubleClickCallback,(LONG_PTR)NM_DBLCLK},
    {XmNreturnCallback,     (LONG_PTR)NM_RETURN},
    {XmNrightClickCallback,       (LONG_PTR)NM_RCLICK},
    {XmNrightDoubleClickCallback, (LONG_PTR)NM_RDBLCLK},
    {XmNsetFocusCallback,         (LONG_PTR)NM_SETFOCUS},
    {XmNkillFocusCallback,        (LONG_PTR)NM_KILLFOCUS},
    };

    const TCHAR* name = NULL;
    LONG_PTR   code = event.getNotification();
    for(int i = 0; i<XtNumber(table); i++) {
      if(table[i].value == code) {
        name = table[i].name;
        break;
      }
    }
    return name;
  }

protected:
// EventHandler for message WM_NOTIFY.
  virtual long notify(Event& event) 
  {
    NMHDR* hdr  = (NMHDR*)event.getLParam();
    HWND sender  = hdr->hwndFrom;

    View* view = (View*)Application::lookup(sender);
    const TCHAR* name = NULL;
  
    if(view) {
      name = isCommonNotification(event);
      if(name == NULL) {
        name = view->getCallbackName(event);
      }
      if(name) {
        callCallback(name, (Key)sender, NULL, event);
      }
    }
    // Return a result of callback.
    return event.getResult();
  }

public:
  //2012/06/22
  // Used for Modeless and Modal Dialog.
  Composite(View* parent)
    :View()
  {
    create(parent);
  }

public:
// 2001/03/11
// Used for Modeless and Modal Dialog.
  virtual Boolean create(View* parent)
  {
    Boolean rc = View::create(parent);

    hicon    = NULL;
    hfocus    = NULL;
    pageIncrement = 16;
    lineIncrement = 2;
    enableThumbTrack =False;
    layoutManager = NULL;

    //2008/06/25
    setDefaultFont();

    addEventHandler(WM_COMMAND, this, 
      (Handler)&Composite::command, NULL);
    addEventHandler(WM_NOTIFY, this, 
      (Handler)&Composite::notify, NULL);

    addEventHandler(WM_HSCROLL, this,
      (Handler)&Composite::horizScroll, NULL);
    addEventHandler(WM_VSCROLL, this,
      (Handler)&Composite::vertScroll, NULL);
    addEventHandler(WM_SIZE, this,
      (Handler)&Composite::size, NULL);

    return rc;
  }

public:
  //2012/06/23
  Composite(View* parent, const TCHAR* name, Args& args)
  :View(),
    hicon(NULL),
    hfocus(NULL),
    pageIncrement(16),
    lineIncrement(2),
    enableThumbTrack(False),
    layoutManager(NULL)
  {
    create(parent, name, args);
  }

public:
  virtual Boolean create(View* parent, const TCHAR* name, Args& args)
  {
    //<modified date="2001/03/11">
    Boolean rc = View::create(parent, name, args.set(styles(), stylesCount()));

    hicon   = NULL;
    hfocus  = NULL;
    pageIncrement = 16;
    lineIncrement = 2;
    enableThumbTrack = False;
    layoutManager = NULL;
    //</modified>

    //2008/06/25
    setDefaultFont();

    addEventHandler(WM_COMMAND, this, 
         (Handler)&Composite::command, NULL);
    addEventHandler(WM_NOTIFY, this, 
         (Handler)&Composite::notify, NULL);

    addEventHandler(WM_HSCROLL, this,
      (Handler)&Composite::horizScroll, NULL);
    addEventHandler(WM_VSCROLL, this,
      (Handler)&Composite::vertScroll, NULL);
    addEventHandler(WM_ACTIVATE, this,
      (Handler)&Composite::activate, NULL);

    addEventHandler(WM_SIZE, this,
      (Handler)&Composite::size, NULL);

    setValues(args);

    addCallback(XmNhorizScrollCallback, (Key)getWindow(), this,
      (Callback)&Composite::doHorizScroll, NULL);
    addCallback(XmNvertScrollCallback, (Key)getWindow(), this,
      (Callback)&Composite::doVertScroll,  NULL);
    return rc;
  }

public:
  ~Composite()
  {
    //Don't delete the layoutManager;

    if(hicon) {
      DestroyIcon(hicon);
      hicon = NULL;
    }
  }

public:
  //2009/09/07
  // Specifiy an Icon Id in resource file(*.rc)
  bool setIcon(int iconId)
  {
    bool rc = false;
    HINSTANCE hInst = getInstanceHandle();

    HICON hLoadedIcon = LoadIcon(hInst, MAKEINTRESOURCE(iconId));
    if (hLoadedIcon) {

      hicon = hLoadedIcon;
      send(WM_SETICON, ICON_SMALL, (LPARAM)hicon);
      send(WM_SETICON, ICON_BIG, (LPARAM)hicon);
      rc = true;
    }
    return rc;
  }

public:
  //2009/09/07
  // Specifiy an Icon Id in resource file(*.rc)
  bool setIcon(const TCHAR* iconName)
  {
    bool rc = false;
    HINSTANCE hInst = getInstanceHandle();

    HICON hLoadedIcon = LoadIcon(hInst, iconName);
    if (hLoadedIcon) {

      hicon = hLoadedIcon;
      send(WM_SETICON, ICON_SMALL, (LPARAM)hicon);
      send(WM_SETICON, ICON_BIG, (LPARAM)hicon);
      rc = true;
    }
    return rc;
  }


protected:
  virtual long activate(Event& event)
  {
    if(LOWORD(event.getWParam()) == WA_INACTIVE) {
      hfocus = getFocus();
    }
    else if (hfocus) {
      setFocus(hfocus);  
    }
    return NULL;
  }

public:
  void layout(int x, int y, int w, int h)
  {
    if (getParent() != NULL) {
      reshape(x, y, w, h);
    }
    if (layoutManager) {
      layoutManager->layout(0, 0, w, h);
    }
  }

protected:
  virtual long size(Event& event) 
  {
    int w, h;
    event.getSize(w, h);
    int x, y;
    getLocation(x, y);
    
    layout(x, y, w, h);

    return 0L;
  }

public:
  void setValues(Args& args)
  {
    View::setValues(args);

    updateStyle(args, styles(), stylesCount());

    LONG_PTR  val;
    if(args.get(XmNpageIncrement, &val))
      pageIncrement = (int)val;
    if(args.get(XmNlineIncrement, &val))
      lineIncrement = (int)val;

    if(args.get(XmNenableThumbTrack, &val))
      enableThumbTrack = (BOOL)val;

    int max, min;
    int type = SB_HORZ;
    getScrollRange(type, &min, &max);
    if(args.get(XmNhorizScrollMaximum, &val))
      setScrollRange(type, min, (int)val);
    getScrollRange(type, &min, &max);
    if(args.get(XmNhorizScrollMinimum, &val))
      setScrollRange(type, (int)val, max);

    type = SB_VERT;
    getScrollRange(type, &min, &max);
    if(args.get(XmNvertScrollMaximum, &val))
      setScrollRange(type, min, (int)val);
    getScrollRange(type, &min, &max);
    if(args.get(XmNvertScrollMinimum, &val))
      setScrollRange(type, (int)val, max);

    if(args.get(XmNhorizThumbPosition, &val))
      setScrollPos(SB_HORZ, (int)val);
    if(args.get(XmNvertThumbPosition, &val))
      setScrollPos(SB_VERT, (int)val);

    // Extract an icon from *.ico or *.exe and set it.
    // CreateIcon API will be called;

    if(args.get(XmNiconFile, &val)) {
      TCHAR* name  = (TCHAR*)val; // file name
      get(XmNinstance, &val);
      HICON temp = ::ExtractIcon((HINSTANCE)val, name, 0);
      if(temp) {
        setClassLong(GCLP_HICON, (LONG_PTR)temp);
        if(hicon) ::DestroyIcon(hicon);
        hicon = temp;
      }
    }
    // Get an icon handle and set it.
    if(args.get(XmNwindowIcon, &val)) {
      //setClassLong(GCLP_HICON, (LONG_PTR)val); 
      //2017/09/10
      send(WM_SETICON, ICON_SMALL, (LPARAM)val);
      send(WM_SETICON, ICON_BIG, (LPARAM)val);      
    }
  }

public:
  void realize()
  {
    show(SW_SHOWNORMAL);
  }

protected:
  const TCHAR* findCallbackName(Event& event, HWND child)
  {
    TCHAR name[1024];
    ::GetClassName(child, name, CountOf(name)); //2012/06/21

    if(strcmp(name, _T("Button")) == 0) {
      Button button(NULL, child);
      return button.getCallbackName(event);
    }  
    if(strcmp(name, _T("Edit")) == 0) {
      Text  text(NULL, child);
      return text.getCallbackName(event);
    }  
    if(strcmp(name, _T("ListBox")) == 0) {
      ListBox listbox(NULL, child);
      return listbox.getCallbackName(event);
    }  
    if(strcmp(name, _T("ComboBox")) == 0) {
      ComboBox combobox(NULL, child);
      return combobox.getCallbackName(event);
    }
  // Add lines here for new controls. 
  //
    return XmNactivateCallback;    
  }

public:
  void  getValues(Args& args)
  {
    View::getValues(args);
    getStyle(args, styles(), stylesCount());

    int num   = args.getCount();
    Arg* arg  = args.getArgList();
    int max, min;

    for(int i = 0; i<num; i++) {
      const TCHAR*    name = arg[i].name;
      LONG_PTR* val  = (LONG_PTR*)arg[i].value;
      if(name == XmNpageIncrement) {
        *val =  lineIncrement;
        continue;
      }
      if(name == XmNlineIncrement) {
        *val =  lineIncrement;
        continue;
      }
      if(name == XmNhorizScrollMaximum) {
        getScrollRange(SB_HORZ, &min, &max);
        *val = max;
        continue;
      }
      if(name == XmNhorizScrollMinimum) {
        getScrollRange(SB_HORZ, &min, &max);
        *val = min;
        continue;
      }
      if(name == XmNhorizThumbPosition) {
        *val = (LONG_PTR)getScrollPos(SB_HORZ);        
        continue;
      }
      if(name == XmNvertScrollMaximum) {
        getScrollRange(SB_VERT, &min, &max);
        *val = max;
        continue;
      }
      if(name == XmNvertScrollMinimum) {
        getScrollRange(SB_VERT, &min, &max);
        *val = min;
        continue;
      }
      if(name == XmNvertThumbPosition) {
        *val = (LONG_PTR)getScrollPos(SB_VERT);        
        continue;
      }
    }
  }

public:
  void setScrollExtent(UINT w, UINT h)
  {
    extent.set(w, h);
    justifyScrollRange();
  }  

public:
  void justifyScrollRange()
  {
    RECT r;
    getClientRect(&r);
    int maxx, maxy;
    UINT w, h;
    extent.get(&w, &h); 
    maxx = w - (r.right - r.left);
    maxy = h - (r.bottom - r.top);
    if(maxx < 0) maxx = 0;
    //<modified date="2000.05.09">
    //if(maxx < 0) maxy = 0;
    if(maxy < 0) maxy = 0;
    //</modified>

    setScrollRange(SB_HORZ, 0, maxx);
    setScrollRange(SB_VERT, 0, maxy);

    // Justify
    getClientRect(&r);
    maxx = w - (r.right - r.left);
    maxy = h - (r.bottom - r.top);
    if(maxx < 0) maxx = 0;
    //<modified date="2000.05.09">
    //if(maxx < 0) maxy = 0;
    if(maxy < 0) maxy = 0;
    //</modified>

    setScrollRange(SB_HORZ, 0, maxx);
    setScrollRange(SB_VERT, 0, maxy);
  }


public:
  BOOL getScrollInfo(int type, SCROLLINFO* scInfo) 
  {
    BOOL rc = FALSE;
    if(scInfo) {
      scInfo->cbSize = sizeof(SCROLLINFO);
      rc  = ::GetScrollInfo(getWindow(), type, scInfo);
    }
    return rc;
  }
  
public:
  int  setScrollInfo(int type, SCROLLINFO* scInfo) 
  {
    int pos = 0;
    if(scInfo) {
      scInfo->cbSize = sizeof(SCROLLINFO);
      pos = ::SetScrollInfo(getWindow(), type, scInfo, TRUE);
    }
    return pos;
  }

public:
  void getPreferredSize(Dimension& d)
  {
    if (layoutManager == NULL) {
      View::getPreferredSize(d);
    }
    else {
      int w, h;
      getSize(w, h);
      layoutManager -> getExtension(d, w, h);
    }
  }

public:
  void pack()
  {
    int w, h;
    getSize(w, h);
    send(WM_SIZE, 0, MAKELONG(w, h));
  }

  void  add(View* view) {
      layoutManager->add(view);
  }

  void  add(View* view, int pos) {
      layoutManager->add(view, pos);
  }

  void  add(View& view) {
      layoutManager->add(&view);
  }
  void  add(View& view, int pos) {
      layoutManager->add(&view, pos);
  }

  LayoutManager* getLayoutManager() { return layoutManager; }

  void  raise() {
      ::BringWindowToTop(getWindow());
  }

  void  setLayout(LayoutManager* layout) {
      layoutManager = layout;
  }


  virtual BOOL  translate(MSG*  msg) { 
    return FALSE;
  }

  int    dirList(const TCHAR* filename, int idList, int idStatic, 
          UINT fileType) {
      return ::DlgDirList(getWindow(), (TCHAR*)filename, idList, 
        idStatic, fileType);
  }
  int    dirListComboBox(const TCHAR* pathName, int idCombo, int idStatic, 
          UINT fileType) {
      return ::DlgDirListComboBox(getWindow(), (TCHAR*)pathName, 
        idCombo,  idStatic, fileType);
  }

  int    dirSelect(const TCHAR* pathName, int len, int idList) {
      return ::DlgDirSelectEx(getWindow(), (TCHAR*)pathName, len, idList);
  }

  int    dirSelectComboBox(const TCHAR* pathName, int len, int idCombo) {
      return ::DlgDirSelectComboBoxEx(getWindow(), (TCHAR*)pathName, 
        len, idCombo);
  }

  int    getItemText(int id, TCHAR* buffer, int max) {
        return ::GetDlgItemText(getWindow(), id, buffer, max);
  }
  int    getItemInt(int id, BOOL* translated, BOOL sign) {
        return ::GetDlgItemInt(getWindow(), id,
        translated, sign);
  }
  HWND  getItem(int id) {
        return ::GetDlgItem(getWindow(), id);
  }
  HWND  getNextGroupItem(HWND control, BOOL prev) {
        return ::GetNextDlgGroupItem(getWindow(), control, prev);
  }
  HWND  getNextTabItem(HWND control, BOOL prev) {
        return ::GetNextDlgTabItem(getWindow(), control, prev);
  }

  void  checkButton(int id, UINT flag) {
        ::CheckDlgButton(getWindow(), id, flag);
  }
  void  checkButton(int firstId, int lastId, int checkId) {
        ::CheckRadioButton(getWindow(), firstId, lastId, checkId);
  }
  UINT  isButtonChecked(int id) {
        return ::IsDlgButtonChecked(getWindow(), id);
  }
  LRESULT  sendDlgItem(int id, UINT message, WPARAM wParam, LPARAM lParam) {
      return ::SendDlgItemMessage(getWindow(), id, 
          message, wParam, lParam);
  }
  void  setItemText(int id, const TCHAR* string) {
        ::SetDlgItemText(getWindow(), id, string);
  }
  void  setItemInt(int id, UINT val, BOOL sign) {
        ::SetDlgItemInt(getWindow(), id, val, sign);
  }

  void  getScrollExtent(UINT* w, UINT* h) {
      extent.get(w, h);
  }

public:
  Font*  getDefaultFont() { return &defaultFont; }

public:
  //2011/02/20
  virtual IOleInPlaceFrame* getInPlaceFrame() 
  {
    return NULL;
  }
  
  virtual IOleObject* getOleObject() 
  {
    return NULL;
  }

};

}


Last modified: 5 May 2019

Copyright (c) 2009-2019 Antillia.com ALL RIGHTS RESERVED.