From ks%cs.tut.fi@ed.aiai Thu Nov 18 15:36:31 1993
Received: from castle.ed.ac.uk by aiai.ed.ac.uk; Thu, 18 Nov 93 15:36:25 GMT
Received: from aiai.ed.ac.uk by castle.ed.ac.uk id aa10393; 18 Nov 93 14:39 GMT
Received: from cs.tut.fi by sun3.nsfnet-relay.ac.uk with Internet SMTP 
          id <sg.16389-0@sun3.nsfnet-relay.ac.uk>;
          Thu, 18 Nov 1993 09:15:53 +0000
Received: from karikukko.cs.tut.fi by cs.tut.fi with SMTP 
          id AA24530 (5.65c/IDA-1.4.4 for <wxwin-users@aiai.edinburgh.ac.uk>);
          Thu, 18 Nov 1993 11:15:48 +0200
Received: by karikukko.cs.tut.fi id AA06005 (5.65c/IDA-1.4.4 
          for tane@cs.uta.fi); Thu, 18 Nov 1993 11:15:44 +0200
Date: Thu, 18 Nov 1993 11:15:44 +0200
From: Syst{ Kari <ks@fi.tut.cs>
Message-Id: <199311180915.AA06005@karikukko.cs.tut.fi>
To: wxwin-users@ed.aiai, tane@fi.uta.cs
Subject: Re: member function as wxFunction
Status: RO
Content-Length: 2137
X-Lines: 82

> > is it true that i can't use a member function of a class as a callback
> > function?
> 
> Yes, unless you declare your member function as static. But then you
> don't have this-pointer available in your function.

There is actually a way. The I first define a new button class:

void my_text_callback (wxObject &obj, wxEvent &ev);

// The typedef for a callback member.
// I found something like this from the Solbource OI include files,
// I just copied it without fully understanding the corresponding
// C++ rule.
typedef void (wxObject::callback_member_func)(...);

// A class with a possibility to send callback method calls to other
// objects/
class MyButton: public wxButton {
public:
  wxObject *client_obj;
  callback_member_func* mem_func;
  MyButton(wxPanel *panel, wxObject *client,
	 callback_member_func *mem, char *label) :
  wxButton(panel, my_text_callback, label)
    {
    client_obj = client;
    mem_func = mem;
  };
};
    
void my_text_callback (wxObject &obj, wxEvent &ev) {

/* This is a terrible glude. The member 'mem_func' should
   should defined at the top of inheritance hierarchy */
  MyButton &o = (MyButton &) obj;
  wxObject *to = o.client_obj;
  (to->*(o.mem_func))(obj,ev);
};




Now, I can set a call back which is a object+method pair (see "new MyButton"):

#include <stream.h>
#include <wx.h>
#include "mybutton.h"

wxText *text;


class MyText: public wxText { // A new class with a call-back method
  int n;
  static char *toolkits[4];
public:
  MyText(wxPanel *p, wxFunction f, char *l) : wxText(p,f,l) {
    n = 0;
    this->SetValue(toolkits[0]);
  };
  void Inc() {
    ++n;
    if (n >= sizeof(toolkits) / sizeof(toolkits[0]))
     n = 0;    
    this->SetValue(toolkits[n]);
  }
};
char *MyText::toolkits[4] = {"wxwin", "uit", "oi", "interviews"};

class MyApp: public wxApp {
public:
  wxFrame *OnInit() {
    wxFrame *frame = new wxFrame(NULL,"Button test");
    wxPanel *panel = new wxPanel(frame);
    text = new MyText(panel, NULL,"Toolkit");
    panel->NewLine();

    new MyButton(panel, text, &MyText::Inc, "Push Me2");
    frame->Show(TRUE);
    return frame;
  };
} myApp;

From kingery%cs.tamu.edu@ed.aiai Fri Dec 17 15:45:24 1993
Received: from festival.ed.ac.uk (festival.edinburgh.ac.uk) by aiai.ed.ac.uk; Fri, 17 Dec 93 15:45:05 GMT
Received: from aiai.ed.ac.uk by festival.ed.ac.uk id aa06437;
          17 Dec 93 15:41 GMT
Received: from uk.ac.nsf by aiai.ed.ac.uk; Fri, 17 Dec 93 15:38:34 GMT
Received: from clavin.cs.tamu.edu by sun3.nsfnet-relay.ac.uk with Internet SMTP 
          id <sg.03461-0@sun3.nsfnet-relay.ac.uk>;
          Fri, 17 Dec 1993 15:35:02 +0000
Received: from cs.tamu.edu (sparc39.cs.tamu.edu) by clavin (AA05907);
          Fri, 17 Dec 93 08:52:24 CST
From: Burell David Kingery <kingery@edu.tamu.cs>
Message-Id: <9312171452.AA05907@clavin>
Subject: wxWindows code sample
To: Craig Cockburn <craig@ed.festival>
Date: Fri, 17 Dec 1993 08:59:15 -0600 (CST)
Cc: wxWindows User Discussions <wxwin-users@ed.aiai>
X-Mailer: ELM [version 2.4 PL23]
Mime-Version: 1.0
Content-Type: text/plain; charset=US-ASCII
Content-Transfer-Encoding: 7bit
Content-Length: 5770
Sender: kingery@edu.tamu.cs
X-Lines: 249
Status: RO


Craig,

Here is the first code sample I promised.  It is a list dialog which can swap
between two different lists.  In my application I use it to swap between
active and inactive employees (those still working and those who aren't).

It is a good example of how to use static member functions as wxFunctions
for callbacks.

Here is the .h file for the list dialog.  I have added comments where I
thought they were appropriate so search for //= to find them.

Hope this helps,

David

================================ CODE STARTS HERE ==========================
/* listdiag.h */

#ifndef LISTDIAG_H
#define LISTDIAG_H

class ListDialog
{
  public:
    ListDialog(wxFrame *parent, int x, int y, char *message,
	       char *caption, int count1, char *list1[],
	       int count2, char *list2[], char **choice, int *index);
    ~ListDialog(void);

  private:
    wxDialogBox *dialog;
    wxListBox	*listbox;
    wxButton	*list_select;
    char	*the_selection;
    int		the_index;
    int 	visible_list;
    int 	c1;
    char	**l1;
    int 	c2;
    char	**l2;

//=
// Here are the three member functions which actually do the work I want
// when the callback is invoked.  Since these are member functions they
// will have access to the instance data of the object.

    void	OkCallback(void);
    void	CancelCallback(void);
    void	ListSelectCallback(void);


//=
// Here are three static member functions which are the initial callbacks
// of the class.  These are invoked in the normal manner for wxWindows.

    static void	OnOk(wxObject& the_object, wxEvent& the_event);
    static void	OnCancel(wxObject& the_object, wxEvent& the_event);
    static void OnListSelect(wxObject& the_object, wxEvent& the_event);
};

#endif


Now here is the .cc file for the list dialog.  I'm not real sure if you could
compile and link, but you could give it a try.  I'm now swamped with other
work here at Texas A&M so it's been awhile since I looked at this code.
Once again search for //= to find pertinent comments.




/* listdiag.cc */

/*
 * standard header goes here.
 */

#include <windows.h>

extern "C"
{
#include <stdio.h>
}

#include "wx.h"

#include "listdiag.h"

ListDialog::ListDialog(wxFrame *parent, int x, int y, char *message,
		       char *caption, int count1, char *list1[],
		       int count2, char *list2[], char **choice, int *index)
{
  if (x < 0) x = 300;
  if (y < 0) y = 300;

  wxDialogBox the_dialog(parent, caption, TRUE, x, y, 300, 250);

  dialog = &the_dialog;
  dialog->SetClientSize(300, 250);
  (void)new wxMessage(dialog, message);
  dialog->NewLine();

  listbox = new wxListBox(dialog, NULL, NULL, wxSINGLE,
			  -1, -1, 150, 200,
			  count1, list1);
  visible_list = 1;
  c1 = count1;
  l1 = list1;
  c2 = count2;
  l2 = list2;

  dialog->NewLine();

//=
// Here I am setting the callback function of the button to be the
// static member function ListDialog::OnOk.  The other buttons are 
// the same way.

  wxButton *ok = new wxButton(dialog,
			      (wxFunction)ListDialog::OnOk, "OK");

//=
// Here is where I set the client data of the button to the this pointer.
// This will be used in the static member function to invoke the correct
// method on the correct object.

  ok->SetClientData((char *)this);


  wxButton *button = new wxButton(dialog,
				  (wxFunction)ListDialog::OnCancel,
				  "Cancel");
  button->SetClientData((char *)this);
  list_select = new wxButton(dialog,
			     (wxFunction)ListDialog::OnListSelect,
			     "Show Inactive");
  list_select->SetClientData((char *)this);

  ok->SetDefault();

  dialog->Fit();
  dialog->Centre();
  dialog->Show(TRUE);
  *choice = the_selection;
  *index = the_index;
}

ListDialog::~ListDialog(void)
{
  return;
}

void ListDialog::OnOk(wxObject& the_object, wxEvent& the_event)
{
  ListDialog	*list_dialog;

//=
// Here is a static member function which is used as a wxFunction.
// The wxObject is the button which was pressed and I use it to get
// the client data of the button.  Remember the client data is the
// this pointer of the object of interest.

  list_dialog = (ListDialog *)((wxButton&)the_object).GetClientData();

//=
// Now that I have the correct object, I can invoke the member function
// of the object.

  list_dialog->OkCallback();

  return;
}

void ListDialog::OnCancel(wxObject& the_object, wxEvent& the_event)
{
  ListDialog	*list_dialog;

  list_dialog = (ListDialog *)((wxButton&)the_object).GetClientData();

  list_dialog->CancelCallback();

  return;
}

void ListDialog::OnListSelect(wxObject& the_object, wxEvent& the_event)
{
  ListDialog	*list_dialog;

  list_dialog = (ListDialog *)((wxButton&)the_object).GetClientData();

  list_dialog->ListSelectCallback();

  return;
}

void ListDialog::OkCallback(void)
{
//=
// This is the member function of the object which gets invoked from the
// static member function ListDialog::OnOk.  Now that I'm within a member
// function I can access the instance data of the object as well as invoke
// other member functions.  The other callbacks are handled the same way.

  the_index = listbox->GetSelection();

  if (the_index != -1)
    {
     the_selection = copystring(listbox->String(listbox->GetSelection()));
    }
  else
    {
     the_selection = NULL;
    }

  dialog->Show(FALSE);
  return;
}

void ListDialog::CancelCallback(void)
{
  the_selection = NULL;
  the_index = -1;
  dialog->Show(FALSE);
  return;
}

void ListDialog::ListSelectCallback(void)
{
  listbox->Clear();

  switch (visible_list)
    {
     case 1:
       listbox->Set(c2, l2);
       list_select->SetLabel(" Show Active ");
       visible_list = 2;
       break;

     case 2:
       listbox->Set(c1, l1);
       list_select->SetLabel("Show Inactive");
       visible_list = 1;
       break;
    }

  return;
}

From kingery%cs.tamu.edu@ed.aiai Fri Dec 17 15:50:18 1993
Received: from festival.ed.ac.uk (festival.edinburgh.ac.uk) by aiai.ed.ac.uk; Fri, 17 Dec 93 15:49:58 GMT
Received: from aiai.ed.ac.uk by festival.ed.ac.uk id aa07307;
          17 Dec 93 15:45 GMT
Received: from uk.ac.nsf by aiai.ed.ac.uk; Fri, 17 Dec 93 15:41:10 GMT
Received: from clavin.cs.tamu.edu by sun3.nsfnet-relay.ac.uk with Internet SMTP 
          id <sg.03470-0@sun3.nsfnet-relay.ac.uk>;
          Fri, 17 Dec 1993 15:35:26 +0000
Received: from cs.tamu.edu (sparc39.cs.tamu.edu) by clavin (AA05925);
          Fri, 17 Dec 93 08:53:21 CST
From: Burell David Kingery <kingery@edu.tamu.cs>
Message-Id: <9312171453.AA05925@clavin>
Subject: wxWindows sample code (2)
To: Craig Cockburn <craig@ed.festival>
Date: Fri, 17 Dec 1993 09:00:11 -0600 (CST)
Cc: wxWindows User Discussions <wxwin-users@ed.aiai>
X-Mailer: ELM [version 2.4 PL23]
Mime-Version: 1.0
Content-Type: text/plain; charset=US-ASCII
Content-Transfer-Encoding: 7bit
Content-Length: 10186
Sender: kingery@edu.tamu.cs
X-Lines: 409
Status: REO


Craig,

Here is the second code sample I promised.  It shows how a dialog or a form
can perform their own deletion and cleanup using "delete this".  Look for
//= for any pertinent comments I have made.  I know you won't be able to
compile this file, but you can at least get the general idea.

David

============================ CODE STARTS HERE ================================

/* empdiag.cc */

/*
 * standard header goes here.
 */

#include <windows.h>

extern "C"
{
#include <string.h>
#include <ctype.h>
}

#ifdef wx_motif
#include <Xm/Xm.h>
#include <Xm/Text.h>
#endif

#include "database.h"

#include "wx.h"

#include "employee.h"	// This is the Employee class created by DB group

#include "ma.h"
#include "empdiag.h"

static enum 
  { 
   ERR_NO_ERROR,
   ERR_NULL_EMP_ID,
   ERR_EMP_ID_NOT_UNIQUE,
   ERR_INV_DATE_FORMAT
  };

EmployeeDialog::EmployeeDialog(wxFrame *frame, int x, int y, int w, int h):
	wxFrame(frame, "Add Employee", x, y, w, h)
{
  // Give the employee dialog an icon
  // SetIcon(new wxIcon("ma_icn"));

  // Create the employee object

  the_employee = new Employee();

  // Create the Employee Data Entry Panel

  Layout(w, h);
 
  // Fit the employee dialog frame to its contents

  Fit();
  Center(wxBOTH);

  // Make the employee dialog visible on the screen

  Show(TRUE);
}

//= This is the dtor of the class.  It is responsible for deleting the
//= employee object if one was created.  the_employee is NOT the dialog
//= object.  It is a support object used by this dialog.  I use
//= "delete this" later on in the code to delete the dialog object.
EmployeeDialog::~EmployeeDialog(void)
{
  if (the_employee)
    {
     delete the_employee;
    }
}

void EmployeeDialog::Layout(int w, int h)
{
  wxButton	*button;
#ifdef wx_motif
#ifdef DO_MOTIF
  Widget	handle;
#endif
#endif

  the_panel = (wxPanel *)new wxPanel((wxFrame *)this, -1, -1, w, h);

  the_panel->NewLine();
  the_check_box = new wxCheckBox(the_panel, NULL, "Manager");
  the_panel->NewLine();
  the_panel->NewLine();
  id_entry = new wxText(the_panel, NULL, "ID:          ");
#ifdef wx_motif
#ifdef DO_MOTIF
  handle = (Widget)id_entry->GetHandle();
  XtVaSetValues(handle,
                XmNmaxLength, EMP_ID_LEN,
                XmNcolumns, EMP_ID_LEN + 1,
                NULL);
#endif
#endif
  the_panel->NewLine();
  fn_entry = new wxText(the_panel, NULL, "First Name:  ");
#ifdef wx_motif
#ifdef DO_MOTIF
  handle = (Widget)fn_entry->GetHandle();
  XtVaSetValues(handle,
                XmNmaxLength, EMP_FN_LEN,
                XmNcolumns, EMP_FN_LEN + 1,
                NULL);
#endif
#endif
  the_panel->NewLine();
  mn_entry = new wxText(the_panel, NULL, "Middle Name: ");
#ifdef wx_motif
#ifdef DO_MOTIF
  handle = (Widget)mn_entry->GetHandle();
  XtVaSetValues(handle,
                XmNmaxLength, EMP_MN_LEN,
                XmNcolumns, EMP_MN_LEN + 1,
                NULL);
#endif
#endif
  the_panel->NewLine();
  ln_entry = new wxText(the_panel, NULL, "Last  Name:  ");
#ifdef wx_motif
#ifdef DO_MOTIF
  handle = (Widget)ln_entry->GetHandle();
  XtVaSetValues(handle,
                XmNmaxLength, EMP_LN_LEN,
                XmNcolumns, EMP_LN_LEN + 1,
                NULL);
#endif
#endif
  the_panel->NewLine();
  addr_entry = new wxText(the_panel, NULL, "Address:     ");
#ifdef wx_motif
#ifdef DO_MOTIF
  handle = (Widget)addr_entry->GetHandle();
  XtVaSetValues(handle,
                XmNeditMode, XmMULTI_LINE_EDIT,
                XmNrows, EMP_ADDR_ROWS,
                XmNmaxLength, EMP_ADDR_LEN,
                XmNwordWrap, True,
                XmNcolumns, EMP_ADDR_COLS,
                NULL);
#endif
#endif
// QUESTION: Should we fill in with the current date?
  the_panel->NewLine();
  hd_entry = new wxText(the_panel, NULL, "Hiring Date: ");
  hd_entry->SetClientData((char *)this);
#ifdef wx_motif
#ifdef DO_MOTIF
  handle = (Widget)hd_entry->GetHandle();
  XtVaSetValues(handle,
                XmNmaxLength, EMP_HD_LEN,
                XmNcolumns, EMP_HD_LEN + 1,
                NULL);
#endif
#endif
  the_panel->NewLine();
  the_panel->NewLine();
  the_panel->NewLine();

  // Add the Ok and Cancel buttons setting the client data to point
  // to this project dialog.

  button = (wxButton *)new wxButton(the_panel, 
                                    (wxFunction)EmployeeDialog::OnOk, "Ok");
  button->SetClientData((char *)this);

  button = (wxButton *)new wxButton(the_panel, 
                                    (wxFunction)EmployeeDialog::OnCancel, 
                                    "Cancel");
  button->SetClientData((char *)this);

  // Fit the panel around the information.

  the_panel->Fit();

  return;
}

void EmployeeDialog::OnOk(wxObject& the_object, wxEvent& the_event)
{
  EmployeeDialog	*the_dialog;

  the_dialog = (EmployeeDialog *)((wxButton&)the_object).GetClientData();

  the_dialog->OkCallback();

  return;
}

void EmployeeDialog::OnCancel(wxObject& the_object, wxEvent& the_event)
{
  EmployeeDialog	*the_dialog;

  the_dialog = (EmployeeDialog *)((wxButton&)the_object).GetClientData();

  the_dialog->CancelCallback();

  return;
}

void EmployeeDialog::OkCallback(void)
{
  Employees	*unique_check;
  char		*string;
  int		looper;
  int		index;
  int		error_status;

  /*
   * The employee id must not be NULL.  Display an error message
   * if the employee id is NULL.
   */

  string = id_entry->GetValue();
  error_status = ERR_NO_ERROR;

  if (strlen(string) == 0)
    {
     error_status = ERR_NULL_EMP_ID;
     delete string;
    }

  if (error_status == ERR_NO_ERROR) 
    {
     /*
      * The employee id must be checked for uniqueness.  If the
      * employee id is not unique, display an error message and
      * disallow the creation of the employee.
      */

     unique_check = new Employees(database, "", "", "", "", "",
                                  "", string, "");

     delete string;

     if (unique_check->size != 0)
       {
        error_status = ERR_EMP_ID_NOT_UNIQUE;
       }

     delete unique_check;
    }

  if (error_status == ERR_NO_ERROR)
    {
     /*
      * The format of the hiring date must be verified to be of the
      * form YYYY/MM/DD.  If it is not in this format display an
      * error message and disallow the creation of the employee.
      */

     string = hd_entry->GetValue();

     if (strlen(string))
       {
        if (strlen(string) != 10)
          {
           error_status = ERR_INV_DATE_FORMAT;
           delete string;
          }
        else if (string[4] != '/' && string[7] != '/')
          {
           error_status = ERR_INV_DATE_FORMAT;
           delete string;
          }
       }
    }

  if (error_status == ERR_NO_ERROR)
    {
     /*
      * Remove the '/' from the date string since this is how
      * it is required in the database.  Also check to insure
      * that each character which is not a '/' is numeric.
      * NOTE:  This function should actually be part of the 
      * Employee class since it requires the date in this abnormal 
      * format.
      */

// TO DO
// Check to insure the date is really valid (valid month and day).
// It will be alright to not check leap years, etc.

     if (strlen(string))
       {
        for (looper = 0, index = 0; 
             looper < EMP_HD_LEN && error_status == ERR_NO_ERROR; looper++)
          {
           if (looper != 4 && looper != 7)
             {
              if (isdigit(string[looper]))
                {
                 the_employee->hiring_date[index] = string[looper];
                 index++;
                }
              else
                {
                 error_status = ERR_INV_DATE_FORMAT;
                }
             }
          } 

        the_employee->hiring_date[index] = '\0';
       }
     else
       {
        the_employee->hiring_date[0] = '\0';
       }

     delete string;
    }

  if (error_status == ERR_NO_ERROR)
    {
//TO DO
     /*
      * Now set the other employee instance data and then
      * invoke the create method.
      */

     string = id_entry->GetValue();
     strcpy(the_employee->emp_id, string);
     delete string;

     string = addr_entry->GetValue();
     strcpy(the_employee->address, string);
     delete string;

     string = fn_entry->GetValue();
     strcpy(the_employee->first_name, string);
     delete string;

     string = mn_entry->GetValue();
     strcpy(the_employee->middle_name, string);
     delete string;

     string = ln_entry->GetValue();
     strcpy(the_employee->last_name, string);
     delete string;
  
     /*
      * Make sure the rest of the stuff in employee
      * is initialized.
      * NOTE: There is no constructor for the Employee class.
      *       There should be one in which the instance data
      *       would be initialized and we wouldn't have to 
      *       worry about it.
      */ 

     the_employee->termination_date[0] = '\0';
     the_employee->user_name[0] = '\0'; 

     the_employee->create(database);

//= Here I have guaranteed that the data is valid and have updated the
//= database so I can now delete the dialog object using "delete this".
//= This will delete the dialog object and all its children user interface
//= objects (if I understand wxWindows correctly).  Once the dialog is
//= deleted, it will no longer be visible on the screen.  One thing I do
//= is always create objects of this class using the new operator.  I don't
//= know if that's a requirement, but it just seems cleaner to me.

     delete this;
    } 

  switch (error_status)
    {
     case ERR_NULL_EMP_ID:
       (void)wxMessageBox("The employee ID cannot be NULL!", 
                          "Data Entry Error");
       break;

     case ERR_EMP_ID_NOT_UNIQUE:
       (void)wxMessageBox("The employee ID must be unique!", 
                          "Data Entry Error");
       break;

     case ERR_INV_DATE_FORMAT:
       (void)wxMessageBox("The hiring date format is YYYY/MM/DD ", 
                          "Data Entry Error");
       break;
    }

  return;
}

void EmployeeDialog::CancelCallback(void)
{
//= When the dialog is cancelled it is deleted.  See the previous comments
//= for more info.

  delete this;

  return;
}

From craig%scot.demon.co.uk@ed.aiai Tue Dec 28 00:29:07 1993
Received: from festival.ed.ac.uk (festival.edinburgh.ac.uk) by aiai.ed.ac.uk; Tue, 28 Dec 93 00:28:55 GMT
Received: from aiai.ed.ac.uk by festival.ed.ac.uk id aa04525; 28 Dec 93 0:23 GMT
Received: from uk.ac.nsf by aiai.ed.ac.uk; Tue, 28 Dec 93 00:24:34 GMT
Received: from post.demon.co.uk by sun3.nsfnet-relay.ac.uk with Internet SMTP 
          id <sg.04713-0@sun3.nsfnet-relay.ac.uk>;
          Tue, 28 Dec 1993 00:21:00 +0000
Received: from scot.demon.co.uk by post.demon.co.uk id aa03792;
          28 Dec 93 0:20 GMT
Date: Tue, 28 Dec 93 00:13:55 GMT
Message-Id: <1835087@scot.demon.co.uk>
From: Craig Cockburn <craig@uk.co.demon.scot>
Reply-To: craig@uk.co.demon.scot
To: wxwin-users@ed.aiai
Subject: Buttons and member functions
Phone: +[44] (0)31 556 9578
X-Mailer: PCElm 1.08
Lines: 26
Content-Length: 1033
X-Lines: 26
Status: RO

Hi,
	I'm wanting to bind the function associated with a button to a 
member function of a class, but no matter what construction I try I can't
get it past the compiler, I keep running into casting problems. Has anyone
been able to do this, if so - how? 

If this isn't currently possible, is there any chance it could be
incorporated into a future kit ? 

this is the code I have at the moment which won't compile due to casting
problems to wxFunction

    void (rule::*toggle_ptr)(); //declare toggle_pointer to point to rule class
    toggle_ptr = &rule::toggle; // Point P to the toggle member function of "rule"
    wxCheckBox *check_box =
               new wxCheckBox(disp_panel, (wxFunction)&rule::toggle);
 
thanks
	Craig


p.s. I'm using the i kit.
------------------------------------------------------------------------------
Craig Cockburn, M.Sc. Student, Napier University, Edinburgh, Scotland
Email: craig@scot.demon.co.uk                       Tel: 031 556 9578
       Sgri\obh thugam 'sa Ga\idhlig ma 'se do thoil e.

From craig@uk.co.demon.scot Mon Jan  3 00:01:43 1994
Received: from uk.ac.nsf by aiai.ed.ac.uk; Mon, 3 Jan 94 00:01:35 GMT
Received: from post.demon.co.uk by sun3.nsfnet-relay.ac.uk with Internet SMTP 
          id <sg.22117-0@sun3.nsfnet-relay.ac.uk>;
          Sun, 2 Jan 1994 23:57:41 +0000
Received: from scot.demon.co.uk by post.demon.co.uk id aa16187;
          2 Jan 94 23:49 GMT
Date: Sun, 02 Jan 94 23:27:26 GMT
Message-Id: <0@scot.demon.co.uk>
From: Craig Cockburn <craig@uk.co.demon.scot>
Reply-To: craig@uk.co.demon.scot
To: jacs@edinburgh.aiai, craig@uk.co.demon.scot
Subject: Re: Buttons and member functions (fwd)
Phone: +[44] (0)31 556 9578
X-Mailer: PCElm 1.08
Content-Length: 4716
X-Lines: 183
Status: REO

Hi,
	I got the following in response to my message about binding 
the function associated to a button with a class's member function. 
Maybe you could implement something along these lines for the next
release so that users can have a cleaner way of calling class member
functions from a button press. 

Also, I'd really like to see a function which returned a boolean 
value to tell me whether a menu item was enabled or not.

thanks
	Craig


Forwarded message follows:

> From ks@cs.tut.fi Tue Dec 28 11:33:38 1993
> Received: from post.demon.co.uk by scot.demon.co.uk with SMTP
> 	id AA72 ; Tue, 28 Dec 93 11:33:32 GMT
> Received: from post.demon.co.uk via puntmail for craig@scot.demon.co.uk;
>           Tue Dec 28  8:25:48 1993
> Received: from cs.tut.fi by post.demon.co.uk id aa05870; 28 Dec 93 8:04 GMT
> Received: from karikukko.cs.tut.fi (karikukko.cs.tut.fi [130.230.2.19]) by cs.tut.fi (8.6.4/8.6.4) with SMTP id KAA10340 for <craig@scot.demon.co.uk>; Tue, 28 Dec 1993 10:04:10 +0200
> Received: by karikukko.cs.tut.fi id AA13640
>   (5.65c/IDA-1.4.4 for craig@scot.demon.co.uk); Tue, 28 Dec 1993 10:04:03 +0200
> Date: Tue, 28 Dec 1993 10:04:03 +0200
> From: Syst{ Kari <ks@cs.tut.fi>
> Message-Id: <199312280804.AA13640@karikukko.cs.tut.fi>
> To: craig@scot.demon.co.uk
> Subject: Re: Buttons and member functions
> Status: R

This might be an answer to your question. The solution is not beatiful.
The support for this kind of notification requires changes to wxObject class.

(C++ is a funny language, isn't it ?)

Three files are included:
 "main.cc" which includes "mybutton.h"
and
 "self.cc", which has a "cleaner" way to do the same thing

MYBUTTON.H:


void my_text_callback (wxObject &obj, wxEvent &ev);

// The typedef for a callback member.
// I found something like this from the Solbource OI include files,
// I just copied it without fully understanding the corresponding
// C++ rule.
typedef void (wxObject::callback_member_func)(...);

// A class with a possibility to send callback method calls to other
// objects/
class MyButton: public wxButton {
public:
  wxObject *client_obj;
  callback_member_func* mem_func;
  MyButton(wxPanel *panel, wxObject *client,
	 callback_member_func *mem, char *label) :
  wxButton(panel, my_text_callback, label)
    {
    client_obj = client;
    mem_func = mem;
  };
};
    
void my_text_callback (wxObject &obj, wxEvent &ev) {

/* This is a terrible glude. The member 'mem_func' should
   should defined at the top of inheritance hierarchy */
  MyButton &o = (MyButton &) obj;
  wxObject *to = o.client_obj;
  (to->*(o.mem_func))(obj,ev);
};




THE "MAIN PROGRAM":
===================
#include <stream.h>
#include <wx.h>
#include "mybutton.h"

wxText *text;


class MyText: public wxText { // A new class with a call-back method
  int n;
  static char *toolkits[4];
public:
  MyText(wxPanel *p, wxFunction f, char *l) : wxText(p,f,l) {
    n = 0;
    this->SetValue(toolkits[0]);
  };
  void Inc() {
    ++n;
    if (n >= sizeof(toolkits) / sizeof(toolkits[0]))
     n = 0;    
    this->SetValue(toolkits[n]);
  }
};
char *MyText::toolkits[4] = {"wxwin", "uit", "oi", "interviews"};

class MyApp: public wxApp {
public:
  wxFrame *OnInit() {
    wxFrame *frame = new wxFrame(NULL,"Button test");
    wxPanel *panel = new wxPanel(frame);
    text = new MyText(panel, NULL,"Toolkit");
    panel->NewLine();

    new MyButton(panel, text, &MyText::Inc, "Push Me2");
    frame->Show(TRUE);
    return frame;
  };
} myApp;



SELF.C
======================================================================
#include <stream.h>
#include <wx.h>



class MyText: public wxText { // A new class with a call-back method
  int n;
  static char *toolkits[4];
public:
  MyText(wxPanel *p, wxFunction f, char *l) : wxText(p,f,l) {
    n = 0;
    this->SetValue(toolkits[0]);
  };
  void Inc() {
    ++n;
    if (n >= sizeof(toolkits) / sizeof(toolkits[0]))
     n = 0;    
    this->SetValue(toolkits[n]);
  }
};
char *MyText::toolkits[4] = {"wxwin", "uit", "oi", "interviews"};

MyText *text;


class MyButton: public wxButton {
public:
  MyText *client_obj;

  void Notify(wxObject &to, wxEvent &e) {
    cerr << "Notify called" << endl;
    client_obj->Inc();
  }
  MyButton(wxPanel *panel, MyText *client, char *label) :
  wxButton(panel, (wxFunction)MyButton::Notify, label) // what is this ??
  {
    client_obj = client;
  };
};


class MyApp: public wxApp {
public:
  wxFrame *OnInit() {
    wxFrame *frame = new wxFrame(NULL,"Button test");
    wxPanel *panel = new wxPanel(frame);
    text = new MyText(panel, NULL,"Toolkit");
    panel->NewLine();

    new MyButton(panel, text,"Push Me2");
    frame->Show(TRUE);
    return frame;
  };
} myApp;






From jvanderw@ca.ubc.ee Thu Jun  2 23:54:42 1994
Received: from festival.ed.ac.uk (festival.edinburgh.ac.uk) by aiai.ed.ac.uk; Thu, 2 Jun 94 23:54:40 BST
Received: from fs1.ee.ubc.ca by festival.ed.ac.uk id aa05664; 2 Jun 94 23:52 BST
Received: from greg.ee.ubc.ca by ee.ubc.ca (0/0 = undefined, so there)
	id PAA05031; Thu, 2 Jun 1994 15:50:49 -0700
From: VANDERWAL JIM HENRY <jvanderw@ca.ubc.ee>
Received: by greg.ee.ubc.ca (4.1/SMI-4.0)
	id AA05258; Thu, 2 Jun 94 15:52:19 PDT
Message-Id: <9406022252.AA05258@greg.ee.ubc.ca>
Subject: Re: Callback Functions in wxWindows
To: J.Smart@ed
Date: Thu, 2 Jun 1994 15:52:18 -0700 (PDT)
X-Mailer: ELM [version 2.4 PL23]
Content-Type: text
Content-Length: 363       
Status: RO


Hello Julian,

Thanks ever so much for the information you sent me
on callbacks in wxWindows.  After a little debugging
I was able to get things to work (Using a static
member function for callback, which in turn calls an 
instantiated member function.)

Cheers,

Jim


Jim Vanderwal
jvanderw@ee.ubc.ca
UBC Dept of Electrical Engineering
Vancouver, BC, Canada
 

From twja@nl.wmt Wed Aug 17 08:14:49 1994
Received: from festival.ed.ac.uk (festival.edinburgh.ac.uk) by aiai.ed.ac.uk; Wed, 17 Aug 94 08:14:46 BST
Received: from aiai.ed.ac.uk by festival.ed.ac.uk id aa10277; 17 Aug 94 8:13 BST
Received: from festival.ed.ac.uk (festival.edinburgh.ac.uk) by aiai.ed.ac.uk; Wed, 17 Aug 94 08:11:34 BST
Received: from sun4nl.nl.net by festival.ed.ac.uk id aa10095; 17 Aug 94 8:11 BST
Received: from wmt by sun4nl.NL.net via EUnet
	id AA06539 (5.65b/CWI-3.3); Wed, 17 Aug 1994 09:11:18 +0200
Received: from umbriel.wmt by wmt.nl (4.1/SMI-4.1)
	id AA00498; Wed, 17 Aug 94 08:51:05 +0200
From: Twan Janssen <twja@nl.wmt>
Message-Id: <9408170651.AA00498@wmt.nl>
Organization: Westmount Technology B.V.
              Olof Palmestraat 24 Delft
              P.O.Box 5063, 2600 GB Delft
              The Netherlands
              Phone: +31 15 141212
              Fax:   +31 15 137048
Subject: Re: Button Callbacks
To: wxwin-users@ed.aiai
Date: Wed, 17 Aug 1994 08:51:04 +0200 (MET DST)
X-Mailer: ELM [version 2.4 PL3]
Mime-Version: 1.0
Content-Type: text/plain; charset=US-ASCII
Content-Length: 1422      
Status: RO

Martin,

I had the same problem. In an class tree like:

myFrame
   |
wxPanel
   |
wxButton

For wxButton and wxPanel you don't need to derive new classes. So the
callback can only be placed in myFrame.

The problem is that the compiler (at least GNU, that I use) still has the
'this' pointer pointing to wxButton, although you execute an operation for
myFrame.
The callback has an argument 'wxButton', but I did not manage to get a
usable value out of this.

I found two solutions:

1.	A callback for every button. Therefore you need extra lines of
	code:

	wxWindow *window;
	wxButton *btn;
	myFrame *myframe;

	btn = (wxButton *)this;
	window = btn->GetGrandParent();
	myframe = (myFrame *)window;

	Disadvantage: this pointer is unavaiable, every reference to the
	frame needs 'frame->' in front of it.

2.	Create an extra operation 'Callback' that calls the real callback. Or
	create one callback that selects and calls the real callbacks.

	Of course you need the same extra lines, in fact you need exactly
	and only these lines in the extra callback. 

	Advantage: 'this' pointer normally available in the methods called 
	from the first callback.

	By the way, when I use the word operation I mean method (Rumbaugh's
	OMT method uses the word operation and we use that method).

	Peter Karp is right when he says that it isn't permitted. You do
	pull some nasty tricks with C++, but it works.

	Good luck!

	Twan.

