#include "sample4.h"

#include XColor_i
#include XMessageBox_i
#include XString_i
#include XControlEvent_i
#include XContainerInfo_i
#include XContainerColumn_i
#include XContainerObject_i
#include XPopupMenu_i
#include XContainerHandler_i
#include XContainerEvent_i
#include XPoint_i
#include XWindowDrag_i
#include XContainerDragEvent_i

#define DEPARTMENT 1
#define WORKER 2

#include <stdlib.h>

MyApp * app;
OOL_ICONHANDLE depIcon, empIcon;
XContainerControl * contLeft, *contRight;

//our class for container object, it is derived from XContainerObject
class MyObjectClass: public XContainerObject
{
   public:
      SHORT type;
      MyObjectClass( XContainerControl * cont, SHORT r, SHORT columns):XContainerObject(cont, columns) {type=r; SetIconHandle(depIcon); }
};

class Employe; //forward

//class Department, it holds the employes
class Department: public MyObjectClass
{
   public:
      Department( char * n, XContainerControl * cont): MyObjectClass( cont, DEPARTMENT, 0) { SetTitle( n ); employe = NULL; count = 0;}
      SHORT count;                       //how much emps
      Employe ** employe;                //list of emps
      void AddEmploye( Employe *);       //methods
      void RemoveEmploye( Employe*);
      void Update( void );
};


//class Employe
class Employe: public MyObjectClass
{
      XString name;                      //the name
      XString position;                  //what function
      ULONG income;                      //how much income
   public:
      Employe( char * n, char * p, ULONG in, XContainerControl * cont);
      SHORT Sort( XContainerObject * p); //sorting method, overridden from XContainerControl
};

//if a new department is choosen, the employe-container must be updated
void Department :: Update( void )
{
   contRight->EnableWindowUpdate( FALSE );  //stop output

   contRight->RemoveAll( FALSE );           //remove all, but dont delete the objects

   int i;

   for( i=0; i < count; i ++)
      contRight->AddObject( employe[i] );   //add the employes of this department

   contRight->Arrange();                    //arrange the objects
   contRight->InvalidateObject();           //repaint
   contRight->EnableWindowUpdate();         //enable output
}


//adds an employe to this department
void Department :: AddEmploye( Employe * e)
{
   count += 1;
   employe = (Employe**) realloc( employe, count * sizeof(void*));
   employe[count-1] = e;
}


//removes an employe from this department
void Department :: RemoveEmploye( Employe * e)
{
   int i;
   BOOL swap = FALSE;

   for( i=0; i < count-1; i++)
      {
         if( e == employe[i] )
            swap = TRUE;
         if( swap )
            employe[i] = employe[i+1];
      }
   count -= 1;
}


//the overridden sorting-method
SHORT Employe :: Sort( XContainerObject * p)
{
   Employe * e = (Employe*) p;     //typecast
   if( e->income == income)        //sorting by income
      return 0;                    //income is equal
   return income > e->income ? -1 : 1; //income is different
}


//constructor employe
Employe::Employe(char*n, char*p, ULONG in, XContainerControl*cont): MyObjectClass( cont, WORKER, 3)
{
   name = n;
   position = p;
   income = in;
   SetColumnData( 0, (char*) name);         //set column-data for detail-view, column 1
   SetColumnData( 1, (char*) position);     //column 2
   SetColumnData( 2, income);               //column 3
   SetIconHandle( empIcon);                 //set the icon
   SetTitle( (char*) name);                 //set the title for icon/name-view
}


//becaus we want to catch events from the containers we must generate a handler
class MyHandler: public XContainerHandler
{
   public:
      ULONG HandleEvent(XContainerEvent*);  //override this method
      MyHandler( XFrameWindow * c): XContainerHandler(c) { ;}
};


//our handler handles here
ULONG MyHandler :: HandleEvent( XContainerEvent * e)
{
   static Department * dropDepartment;

   switch(e->GetEventID())               //what type of event?
      {
         case CON_EMPHASIS:
            if( e->GetWindowID() == CONT_LEFT)
               {
                  Department * d = (Department*) e->GetObject();
                  if( d->QueryEmphasis() & CON_SELECTED)
                     {
                        d->Update( );
                     }
               }
            break;
         case CON_CONTEXTMENU:
            if( e->GetWindowID() == CONT_RIGHT)
               {
                  XPoint p;
                  XResource res( IDM_EMPLOYE, app->GetResourceLibrary());
                  XPopupMenu * menu = new XPopupMenu( &res, (XFrameWindow*) GetWindow());
                  GetWindow()->QueryPointerPos( &p);
                  XRect r;
                  GetWindow()->GetSize( &r);
                  p.SetX( p.GetX() - r.GetX());
                  p.SetY( p.GetY() - r.GetY());
                  menu->Display( &p, IDM_ICON);
               }
               break;
         case CON_DRAGOVER:                             //a flying object about one of our windows
            if( e->GetWindowID() == CONT_LEFT)          //the left container?
               {
                  XContainerDragEvent * dr = (XContainerDragEvent*) e;
                  XContainerObject * o;
                  if( ( o = dr->GetObject()) != NULL)   //is there an object under the mouse?
                     {
                         if( o->QueryEmphasis() & CON_SELECTED)  //yes, the current department
                            dr->SetAcceptMode( DRG_NODROP );     //in this case dont accept the objects
                         else
                            {                                    //here is not the actual department under the mouse
                               dr->SetAcceptMode( DRG_DROP );    //accept the object
                               dr->SetOperation( DRG_MOVE );     //moving is our method
                               dropDepartment = (Department*) o; //this department receive the object if they are dropped
                            }
                     }
                  else
                     dr->SetAcceptMode( DRG_NODROP ); //no object under the mouse, disable drop
               }
               return 0;
         case CON_INITDRAG:                                         //initialize dragging
            if( e->GetWindowID() == CONT_RIGHT)                     //the right container?
              {
                  SHORT i = 0;
                  Employe* obj = (Employe*)e->GetObject();          //which object is under the mouse
                  XContainerControl * c = (XContainerControl*) e->GetWindow();  //get a pointer to the window
                  if(obj->QueryEmphasis() & CON_SELECTED)           //is the object selected?
                     {
                         XContainerObject * o = c->QueryObject();   //count the selected objects
                         do
                            {
                                o = c->QueryObject( o, CON_SELECTED);
                                i++;
                            } while( o );
                     }
                  else
                     i=1;                                           //nothing selected, only one object to drag

                  XWindowDrag wDrag( c, i );                        //our drag-class, give a pointer to the window
                                                                    //and the count of objects to be dragged

                  if( i == 1)                                       //only one (non-selected) object
                     {
                        XDragItem item( c );                        //construct a drag-item
                        wDrag.SetDragItem( &item, 0 );              //add the drag-item at the first position
                        wDrag.AddImage( obj->QueryIconHandle());    //add a image
                     }
                  else
                     {
                        SHORT j=0;                                  //more than one objects to drag
                        XContainerObject * o = obj;
                        do                                   //add all selected objects
                           {
                              XDragItem item( c );            //create a new drag-item
                              wDrag.SetDragItem( &item, j );  //add it at the right position
                              if( j < 5)                      //for the first 5 items an image is added (stretched)
                                 wDrag.AddImage( o->QueryIconHandle(), IMG_ICON | DRG_STRETCH, j * 10, j * 10, 40 - j * 8);
                              o = c->QueryObject( o, CON_SELECTED); //ask for the next selected object
                              j++;
                           } while( o );
                     }
                  if( wDrag.PerformDrag() )                         //do the drag operation
                     {
                        Department * d = (Department*) contLeft->QueryObject(); //dragging was performed corectly
                                                                                //ask for the current department
                        if( d)
                           {
                              if(obj->QueryEmphasis() & CON_SELECTED)           // if the drag-object was selected...
                                {
                                   Employe * e = (Employe*) contRight->QueryObject();
                                   do                                           //..do this for every selected object
                                      {
                                         d->RemoveEmploye( e );                 //remove the emp from the current department
                                         dropDepartment->AddEmploye( e);        //add the emp to the new department
                                         e = (Employe*) contRight->QueryObject( e, CON_SELECTED); //get the next selected object
                                      } while( e );
                                }
                              else                                              //no object selected, method like above
                                {
                                    d->RemoveEmploye( obj );
                                    dropDepartment->AddEmploye( obj );
                                }
                              d->Update( );                                     //update
                           }
                     }
              }
            break;
      }
}


//XContainerControl * commandControl = NULL;


MyAppWindow :: MyAppWindow( XApplication * app, XResource * r ): XFrameWindow( r, "Sample4 - Container", XFrameWindow::defaultDialogStyle | FRM_TASKLIST )
{
   XColor c( COL_PALEGRAY);              //background-color
   SetBackgroundColor( &c);
   XRect re( 100, 100, 600, 400);
   SetSize( &re);                        //size

   XRect r1(10,30,280, 330);             //generate a container
   contLeft = new XContainerControl( this, &r1, CONT_LEFT, WIN_BORDER|WIN_VISIBLE);

   //setup for the container (tree view)
   XContainerInfo info( "Departments", CO_TREE | CO_TITLE | CO_TREELINE );
   contLeft->SetInfo( &info);

   XRect r2( 300,30,280, 330);
   //second container
   contRight = new XContainerControl( this, &r2, CONT_RIGHT, WIN_BORDER | WIN_VISIBLE | CON_MULTIPLESEL );
   //setup (detail view)
   XContainerInfo info2( "Employees", CO_DETAIL | CO_TITLE | CO_DETAILTITLES);
   info2.EnableSorting();                //enable sorting
   contRight->SetInfo( &info2);

   //for detail view we genrate 3 columns
   XContainerColumn * col = new XContainerColumn( contRight, "Name", 0, COL_HORZSEPARATOR | COL_STRING | COL_SEPARATOR, COL_LEFT | COL_FITITLEREADONLY | COL_HORZSEPARATOR | COL_TOP );
   contRight->InsertColumn( col);
   XContainerColumn * col2 = new XContainerColumn( contRight, "Function", 1, COL_SEPARATOR | COL_HORZSEPARATOR | COL_STRING, COL_LEFT | COL_FITITLEREADONLY | COL_HORZSEPARATOR | COL_TOP );
   contRight->InsertColumn( col2, col);
   XContainerColumn * col3 = new XContainerColumn( contRight, "Income", 2, COL_HORZSEPARATOR | COL_ULONG, COL_LEFT | COL_FITITLEREADONLY | COL_HORZSEPARATOR | COL_TOP );
   contRight->InsertColumn( col3, col2);

   //update columns
   contRight->UpdateColumns();

   //generate 3 departments and add them to the left container
   Department * n1 = new Department( "Headquarter", contLeft);
   contLeft->AddObject( n1);             //this object is the root

   Department * n2 = new Department( "Development", contLeft);
   contLeft->AddObject( n2, n1);         //n1 is parent of this object

   Department * n3 = new Department( "Selling", contLeft);
   contLeft->AddObject( n3, n1);         //n1 is parent of this object

   //generate 4 employes
   Employe * e1 = new Employe( "Smith", "Programmer", 900, contRight);
   Employe * e2 = new Employe( "Bond", "Programmer", 500, contRight);
   Employe * e3 = new Employe( "Bold", "Programmer", 800, contRight);
   Employe * e4 = new Employe( "Miller", "Programmer", 600, contRight);
   contRight->InvalidateObject();

   //add the employes to different departments
   n1->AddEmploye( e1 );
   n1->AddEmploye( e2 );
   n2->AddEmploye( e3 );
   n3->AddEmploye( e4 );

   //select the first department to show the content
   n1->Update( );

   //generate a handler for container-events
   MyHandler * h = new MyHandler(this);

   Activate();
}


MyAppWindow :: ~MyAppWindow()
{
}


//we only fill the background
void MyAppWindow :: Draw( void )
{
   FillBackground( );
}


/* here the commands of the popup-menus are posted */
void MyAppWindow :: DoCommand( LONG com)
{
   switch( com )
      {
         case IDM_ICON:                        //the user selected a new view from
         case IDM_DETAIL:                      //the popup-menu
         case IDM_TEXT:
            {
               XContainerInfo info;
               contRight->QueryInfo( &info);
               if(com == IDM_ICON)             //icon-view requested
                  info.SetAttributes( CO_ICON | CO_TITLE );
               if(com == IDM_DETAIL)           //detail-view requested
                  info.SetAttributes( CO_DETAIL | CO_TITLE | CO_DETAILTITLES );
               if(com == IDM_TEXT)             //text-view requested
                  info.SetAttributes( CO_TEXT | CO_TITLE );
               contRight->SetInfo( &info);
               contRight->Arrange();           //re-arrange the container
            }
            break;
      }
}


void MyAppWindow :: DoControl( XControlEvent * event)
{
}


MyApp :: MyApp(): XApplication()
{
   XResource r( 0, GetResourceLibrary());
   app = this;

   //load the icons we need
   depIcon = LoadIcon( "folder.ico", FALSE);
   empIcon = LoadIcon( "person.ico", FALSE);
   window = new MyAppWindow( this, &r );   //create new framewindow (see above)
}


void main ( void)
{
   MyApp * app = new MyApp();  //create a new application
   app->Start();               //let the application work
}
