Clase TMySql de Harbour

Este es un breve resumen de la clase TMySQL.

Una guía de referencia para utilizar sus métodos.

/*
MariaDB/MySql Harbour Contribution Library


TMySQLServer:  manages access to a MySQL server and returns an oServer object
               to which you'll send all your queries;

TMySQLQuery:   a standard query to an oServer with joins. Every query has a
               GetRow() method which on every call returns a TMySQLRow object
               which, in turn, contains requested fields. Query objects convert
               MySQL answers (which is an array of strings) to Clipper level types.
               At present time N (with decimals), L, D, and C Cl*pper types are
               supported.

TMySQLTable:   It's a descendant of a TMySQLQuery and you'll receive it when your
               query has no joins. It adds Update(), Append() and Delete() methods
               which receive a TMySQLRow object and reflect changes to the MySQL
               table from which they come. Please note that TMySQLQuery objects
               don't have these methods, so, if you want to change a row received
               from a TMySQLQuery object you need to construct a valid SQL query
               and submit it to an oServer object.

TMySQLRow:     Every row returned by a SELECT is converted to a TMySQLRow object.
               This object handles fields and has methods to access fields given
               a field name or position.
*/

CLASS TMySQLServer  /* Every available MySQL server */

      DATA  nSocket  /* Connection handle to server
                        (currently pointer to a MYSQL structure) */

      DATA  cServer  /* Server name */

      DATA  cDBName  /* Selected DB */

      DATA  cUser    /*	User accessing db */

      DATA  cPassword /* His/her password */

      DATA  lError /* .T. if occurred an error */

      DATA  cCreateQuery

      METHOD   New(cServer, cUser, cPassword, nPort) /*	Opens connection to a
                                                        server, returns a
                                                        server object */

      METHOD   Destroy() /* Closes connection to server */

      METHOD   SelectDB(cDBName) /* Which data base I will use
                                    for subsequent queries */

      METHOD   CreateTable(cTable, aStruct,cPrimaryKey,cUniqueKey,cAuto)
                                                 /* Create new table using the
                                                    same syntax of dbCreate() */

      METHOD   DeleteTable(cTable) /* Delete table */

      METHOD   TableStruct(cTable) /* Returns a structure array compatible
                                      with clipper's dbStruct() ones */

      METHOD   CreateIndex(cName, cTable, aFNames, lUnique)
                                                 /* Create an index (unique)
                                                    on field name(s) passed as
                                                    an array of strings aFNames */

      METHOD   DeleteIndex(cName, cTable) /* Delete index cName from cTable */

      METHOD   ListDBs()  /* Returns an array with list of data bases available */

      METHOD   ListTables() /* Returns an array with list of
                               available tables in current database */

      METHOD   Query(cQuery) /* Gets a textual query and returns a
                                TMySQLQuery or TMySQLTable object */

      METHOD   NetErr() INLINE ::lError  /* Returns .T. if something went wrong */

      METHOD   Error() /* Returns textual description of last error */

      METHOD   CreateDatabase( cDataBase ) /* Create an New Mysql Database */

      METHOD   sql_Commit() /* Commits transaction */

      METHOD   sql_Rollback() /* Rollbacks transaction */

      METHOD   sql_Version() /* Server version as numeric */

ENDCLASS


CLASS TMySQLQuery  /* Every single query submitted to MySQL server */

      DATA  nSocket /* Connection handle to MySQL server */

      DATA  nResultHandle /* Result handle received from MySQL */

      DATA  cQuery /* Copy of query that generated this object */

      DATA  nNumRows /*	Number of rows available on answer NOTE MySQL is 0 based */

      DATA  nCurRow  /*	I'm currently over row number */

      DATA  lBof

      DATA  lEof

      DATA  lFieldAsData  /* Use fields as object DATA. For compatibility
                             Names of fields can match name of TMySQLQuery/Table
                             DATAs, and it is dangerous. ::lFieldAsData:=.F.
                             can fix it */

      DATA  aRow          /* Values of fields of current row */

      DATA  nNumFields    /* How many fields per row */

      DATA  aFieldStruct  /* Type of each field, a copy is
                             here a copy inside each row */

      DATA  lError        /* .T. if last operation failed */

      METHOD   New(nSocket, cQuery) /* New query object */

      METHOD   Destroy()

      METHOD   End()

      METHOD   Refresh() /* ReExecutes the query (cQuery) so that
                            changes to table are visible */

      METHOD   GetRow(nRow) /* Return Row n of answer */

      METHOD   Skip(nRows) /* Same as clipper ones */

      METHOD   Bof()

      METHOD   Eof()

      METHOD   RecNo()

      METHOD   LastRec()

      METHOD   GoTop()

      METHOD   GoBottom()

      METHOD   GoTO(nRow)

      METHOD   FCount()

      METHOD   NetErr() /* Returns .T. if something went wrong */

      METHOD   Error()  /* Returns textual description of
                           last error and clears ::lError */

      METHOD   FieldName(nNum)

      METHOD   FieldPos(cFieldName)

      METHOD   FieldGet(cnField)

      METHOD   FieldLen(nNum) /* Length of field N */

      METHOD   FieldDec(nNum) /* How many decimals in field N */

      METHOD   FieldType(nNum) /* Clipper type of field N */

ENDCLASS


CLASS TMySQLTable   /* A query without joins; Insert() e Delete() rows are allowed.
                       NOTE: it's always a SELECT result, so it will contain a
                       full table only if SELECT * FROM ... was issued */

      DATA  cTable  /* Name of table */

      DATA  aOldValue	/* Keeps a copy of old value */

      METHOD   New(nSocket, cQuery, cTableName)

      METHOD   GetRow(nRow)

      METHOD   Skip(nRow)

      METHOD   GoTop()

      METHOD   GoBottom()

      METHOD   GoTo(nRow)

      METHOD   Update(oRow, lOldRecord, lRefresh) /* Gets an oRow and
                                                     updates changed fields */

      METHOD   Save()

      METHOD   Delete(oRow, lOldRecord, lRefresh) /* Deletes passed row
                                                     from table */

      METHOD   Append(oRow, lRefresh) /* Inserts passed row into table */

      METHOD   GetBlankRow( lSetValues ) /* Returns an empty row with
                                            all available fields empty */

      METHOD   SetBlankRow() /*	Compatibility */

      METHOD   Blank()

      METHOD   FieldPut(cnField, Value) /* Field identifier, not only a number */

      METHOD   Refresh()

      METHOD   MakePrimaryKeyWhere() /*	Returns a WHERE x=y statement which
                                        uses primary key (if available) */

ENDCLASS


CLASS TMySQLRow   /* Every single row of an answer */

      DATA  aRow  /* A single row of answer */

      DATA  aDirty  /* Array of booleans set to .T. if
                       corresponding field of aRow has been changed */

      DATA  aOldValue   /* If aDirty[n] is .T. aOldValue[n] keeps a copy of changed
                           value if aRow[n] is part of a primary key  */

      DATA  aOriValue  /* Original values ( same as TMySQLtable:aOldValue ) */

      DATA  aFieldStruct /* Type of each field */

      DATA  cTable  /* Name of table containing this row,
                       empty if TMySQLQuery returned this row */

      METHOD	New(aRow, aFStruct, cTableName) /* Create a new Row object */

      METHOD   FieldGet(cnField)  /* Same as clipper ones, but FieldGet()
                                     and FieldPut() accept a string as */

      METHOD   FieldPut(cnField, Value) /* Field identifier, not only a number */

      METHOD   FieldName(nNum)

      METHOD   FieldPos(cFieldName)

      METHOD   FieldLen(nNum)  /* Length of field N */

      METHOD   FieldDec(nNum)  /* How many decimals in field N */

      METHOD   FieldType(nNum) /* Clipper type of field N */

      METHOD   MakePrimaryKeyWhere() /*	Returns a WHERE x=y statement
                                        which uses primary key (if available) */

ENDCLASS

Dolphin para MySQL

A continuación un acercamiento breve a lo que ofrece la clase Dolphin de Daniel García Gil, para la gestión de bases de datos MySQL.

Mayor información y descargas en https://bitbucket.org/danielgarciagil/tdolphin/downloads

CLASS TDolphinSrv

   CLASSDATA nQueryId
   CLASSDATA nServerId  INIT 1

   DATA bDecrypt       /*codeblock to evaluate in connect process (C Level), ;
                         to decrpty Host, User, Password and Database*/
   DATA bOnError       /*Custom manager error message
                         ( Self, nError, lInternal ) */
   DATA bOnBackUp      /*codeblock to evaluate in backup process*/

   DATA bOnRestore     /*codeblock to evaluate in restore process*/
   DATA bOnMultiQry    /*codeblock to evaluate for each Query in METHOD MultiQuery*/
   DATA bOnAfterQuery   /*codeblock to evaluate after execute a MySql Statement*/
#ifdef DEBUG
   DATA bDebug         /*codeblock to evaluate for each Query, ;
                         Arg cQuery, ProcName( 1 ), ProcLine( 1 )*/
#endif

   DATA cDBName        /*Data base selected*/
   DATA cPassword      /*Data contains the password for user*/
   DATA cHost          /*Host name, may be either a host name or an IP address */
   DATA cUser          /*DAta contains the user's MySQL login ID*/
   DATA cNameHost

   DATA cBuild     INIT "25-Jan-12 6:41:25 AM"

   DATA hMysql         /*MySQL connection handle*/
                       
   DATA lReConnect     
   
   DATA Cargo          /*For programmer use*/
                       
   DATA lError         /*Error detection switch*/
                  
   DATA nFlags         /*Client flags*/
   DATA nInternalError /*error manager, no come from MySQL*/
   DATA nPort          /*value is used as the port number for the TCP/IP connection*/
   
   DATA aQueries       /*Array queries actives*/
   
   METHOD New( cHost, cUser, cPassword, nPort, nFlags, bOnError, cDBName )
   METHOD ssl( cHost, cUser, cPassword, nPort, nFlags, bOnError, cDBName, cSslKeyFile, ;
                                      cSslCertFile, cSslCaFile, cCertsPath, cSslCipher )
   
   METHOD AddUser( cHost, cUser, cPassword, cDb, lCreateDB, acPrivilegs, cWithOption )
                              /*The AddUser() enables system administrators to grant privileges ;
                                to MySQL user accounts. 
                                AddUser also serves to specify other account characteristics such ;
                                as use of secure connections and limits on access to server resources. 
                                To use AddUser(), you must have the GRANT OPTION privilege, 
                                and you must have the privileges that you are granting.*/
                                
   METHOD AddQuery( oQuery )          INLINE AAdd( ::aQueries, oQuery )
                              /*used internally*/

   METHOD Backup( aTables, cFile, lDrop, lOver, nStep, cHeader, cFooter, lCancel )

   METHOD BeginTransaction()          INLINE ::SqlQuery( "BEGIN" )    
   
   METHOD Call( /*...*/ )  /*run a function/procedure with n parameters, 
                             1st parameter must be a function/procedure name,
                             this method does not return any result in query */

   METHOD Debug( cText )     INLINE  If(  ::bDebug != NIL, Eval( ::bDebug, cText, ;
                                                  ProcName( 1 ), ProcLine( 1 ) ), )
   METHOD ReturnCall( /*...*/ ) /*same METHOD Call but this return a result set*/
   
   METHOD ChangeEngine( cTable, cType )  INLINE ::SqlQuery( "ALTER TABLE " + D_LowerCase( cTable ) + ;
                                                             " ENGINE = " + D_LowerCase( cType ) )

   METHOD ChangeEngineAll( cType )  
   
   METHOD CheckError( nError )

   METHOD CloseQuery( nId )
   
   METHOD CloseAllQuery()             

   METHOD Compact( cTable )
 
   METHOD Connect( cHost, cUser, cPassword, nPort, nFlags, cDBName )    
                     /*to establish a connection to a MySQL database engine running on server*/

   METHOD SSLConnect( cHost, cUser, cPassword, nPort, nFlags, cDBName, cSslCaFile, ;
                                             cSslCertFile, cSslKeyFile, cSslCipher )


   METHOD CommitTransaction()       INLINE ::Debug( "COMMITED" ),  MySqlCommit( ::hMySql ) == 0 
                              /*Commits the current transaction.*/

   METHOD CreateForeign( cName, cTabParent, aIndColName, cTabChild, aIndColRef, ;
                         lOnDelete, nActionDelete, lOnUpdate, nActionUpdate ) 
                              /* Create Foreign Key cName Symbol Name */
   
   METHOD CreateIndex( cName, cTable, aFNames, nCons, nType )                              
   
   METHOD CreateInfo( cTable )
                              
   METHOD CreateTable( cTable, aStruct, cPrimaryKey, cUniqueKey, cAuto, cExtra )
                              /*creates a table with the cTable name*/

   METHOD DBCreate( cName, lIfNotExist, cCharSet, cCollate )
                              /* Create Database in current active connection*/

                                
   METHOD DBExist( cDB )  INLINE If( ! Empty( cDB ), Len( ::ListDBs( D_LowerCase( cDB ) ) ) > 0, .F. )
                              /* verify is Data Base exist, return logical value*/

   METHOD DeleteDB( cDB, lExists )
                              /*Delete Tables*/
                              
   METHOD DeleteForeign( cName, cTable ) 
                              /*Delete Foreign*/

   METHOD DeleteIndex( cName, cTable )         
                              /*Delete Index*/                     
   
   METHOD DeleteTables( acTable, lExists )
                              /*Delete Tables*/
                              
   METHOD DropUser( cUser )             INLINE ::SqlQuery( "DROP USER " + cUser )
                              /*Drop User*/
   
   METHOD Embedded( cDataBase, aOptions, aGroups )
   
   METHOD End()
   
   METHOD ErrorTxt()          INLINE  If( ::hMysql != NIL, MySqlError( ::hMysql ), "" )
                              /* Returns a string containing the error message for 
                                 the most recently invoked API function that failed.*/
   
   METHOD ErrorNo()           INLINE ::lError := .F., MySqlGetErrNo( ::hMysql )
                              /* Returns the error code for the most recently invoked 
                                API function that can succeed or fail. 
                                A return value of zero means that no error occurred.*/
                                
   METHOD Execute( cQuery, uParams )   INLINE ::SqlQuery( cQuery, uParams )
   
   METHOD ExecuteScript( cFile ) 

   METHOD GetAutoIncrement( cTable )
                                /*Retrieve next Auto increment value in specified table;
                                 in current database selected*/   

   METHOD GetEngine( cTable, cSchema )
      
   METHOD GetServerInfo()       INLINE If( ::hMysql != NIL, MyServerInfo( ::hMysql ), "" ) 
                                /*Returns a string that represents the server version number.*/

   
   METHOD GetClientInfo()       INLINE If( ::hMysql != NIL, MyClientInfo(), "" ) 
                           /*Return a string that represents the MySQL client library version.*/

   
   METHOD GetPrivileges()
   
   METHOD GetQueryId()   
   

   METHOD GetRowsFromTable( cTable )
                               /*Retrieve total row avalaible in  specified table;
                                in current database selected*/    

   METHOD GetRowsFromQry( oQuery )
                               /*Retrieve total row avalaible in  specified query;
                                in current database selected*/    
   
   METHOD hInsert( ctable, hValues, cDuplicateKey )
   
   METHOD Insert( cTable, aColumns, aValues, cDuplicateKey )
                              /*inserts new rows into an existing table.*/
                              
   METHOD InsertFromDbf( cTable, cAlias, nLimit, aStruct, bOnInsert, cDuplicateKey ) 
                              /*insert new rows into an existing table from DBF file,
                                the table should be contain same fieldname that DBF */

   METHOD IsAutoIncrement( cField, cTable )
                              /* Verify is a field is Auto Increment*/
   
   METHOD LastDownData( cTable, cCol, uDef )
   
   METHOD LastInsertID()      /*Returns the first automatically generated value that was 
                                set for an AUTO_INCREMENT column by the most recently 
                                executed INSERT statement to affect such a column.*/
   
   METHOD ListDBs( cWild )    /* Returns a array set consisting of database names on the server 
                                 that match the simple regular expression specified by the wild  
                                 parameter.wild may contain the wildcard characters n++%n++ or 
                                 n++_n++, or may be a "" to match all databases.*/
   
   METHOD ListTables( cWild ) /* Returns a array set consisting of tables names in current satabase 
                                 that match the simple regular expression specified by the wild 
                                 parameter. wild may contain the wildcard characters n++%n++ 
                                 or n++_n++, or may be a "" to match all tables.*/
                                 
   METHOD MultiQuery( aQuery, lTransaction )
   
   METHOD NextResult() INLINE mysql_next_result( ::hMysql )
                               /* Use only for MULTIPLE STATEMENT or stored PROCEDURE/FUNCTION */   
   
   METHOD Ping()       INLINE If( MySqlPing( ::hMysql ) > 0, ( ::CheckError(), .F.), .T. )
                   /* Checks whether the connection to the server is working. 
                      If the connection has gone down and auto-reconnect is enabled an attempt 
                      to reconnect is made. If the connection is down and auto-reconnect is disabled,
                      ::ping() returns an error.*/

   METHOD Query( cQuery )   

   METHOD ReConnect()

   METHOD RenameUser( cFromUser, cServer, cRename )
                              /*Rename User*/
                              
   METHOD Restore( cFile, lCancel )                              

   METHOD RevokePrivileges( cHost, cUser, cDB, acPrivilegs )
                              /*The RevokePrivileges() enables system administrators 
                                              to revoke privileges from MySQL accounts.*/
   

   METHOD RollBack()        INLINE ::Debug( "ROLLBACK" ), MySqlRollBack( ::hMysql )
                              /* Rolls back the current transaction.*/
                             
   METHOD SelectDB( cDBName ) 
                              /*Select data base in current active connection*/
   

   METHOD SelectTable( aColumns, aTables, cWhere, cGroup, cHaving, cOrder, cLimit, lWithRoll )
   
   METHOD SetNameServer( cName )
   
   METHOD SetMultiStatement( lOnOf ) INLINE SetMultiStatement( ::hMysql, lOnOf )
   
   METHOD SqlQuery( cQuery, uParams )  /*Executes the SQL statement pointed to by cQuery, 
                              Normally, the string must consist of a single SQL statement 
                              and you should not add a terminating semicolon (n++;n++) 
                              or \g to the statement. If multiple-statement execution has 
                              been enabled, the string can contain several statements 
                              separated by semicolons.*/
 
   METHOD TableExist( cTable )  INLINE If( ! Empty( cTable ), ;
                                       Len( ::ListTables( D_LowerCase( cTable ) ) ) > 0, .F. )
                              /* verify is table exist, return logical value*/ 

   METHOD TableInitValues( cTable )
   
   METHOD TableStructure( cTable )  
   
   METHOD hUpdate( cTable, hValues, cWhere ) 
                             /*update specific rows into an existing table from a hash, ;
                               the index of hash shold be field name.*/
                             
   METHOD Update( cTable, aColumns, aValues, cWhere )
                             /*update specific row into an existing table.*/
                                 
ENDCLASS



CLASS TDolphinQry

   DATA aColumns,;    // query active columns (select)
        aTables,;     // query active tables
        aRow,;        // info currect record selected
        aStructure    // type of each field, a copy is here a copy inside each row
   DATA aOldRow       // Value copy
   DATA aRowOriginal  // Original data values (without changes, like return from mysql)

   DATA bBof,;        //codeblock to evaluate if the value is the first row
        bEof,;        //codeblock to evaluate if the value is the last row
        bOnFillArray,;//codeblock to evaluate while is filling array
        bOnChangePage,; //codeblock to evaluate when paginmation is activated and change page
        bOnLoadQuery,;  //codeblock to evaluate before load new Query
        bOnNewFilter   //codeblock to evaluate before set new query, 
                       //should return .t./.f. to call BuildQuery

   DATA cQuery,;        // copy of query that generated this object
        cWhere,;        // copy of WHERE command
        cGroup,;        // copy of GROUP BY command
        cHaving,;       // copy of HAVING command
        cOrder,;        // copy of OREDER BY command
        cLimit          // copy of LIMIT command

   DATA Cargo           // For programmer use
   
   DATA hOldRow                // Hash Last row selected
   DATA hResult                 
   DATA hRow                   // Hash current row selected

   DATA lBof                   // Begin of query, compatibility with dbf*/
   DATA lEof                   // End of Query, compatibility with dbf*/
   DATA lAppend
   DATA lPagination
   DATA lInverted              // Seek in inverted order

   DATA nFCount                // number of fields in the query
   DATA nRecCount              // number of rows in the current query
   DATA nRecNo                 // Current query row position
   DATA nQryId
   
   
   //Paginations datas
   DATA nCurrentPage           // Current page
   DATA nTotalRows             // Total row without limits
   DATA nPageStep              // total rows for page
   DATA nMaxPages              // Max pages avalaible in query
   DATA nCurrentLimit          // Current limit value
   
   DATA oServer
   DATA oRow


   METHOD New( cQuery, oServer )
   METHOD End()        INLINE ::oServer:CloseQuery( ::nQryId ), ::oRow := NIL

   METHOD Bof()        INLINE ::lBof  
   
   METHOD BuildDatas( cQuery ) 
   METHOD BuildDataWhere()     /* build a where with oldrow values */
   
   METHOD BuildQuery( aColumns, aTables, cWhere, cGroup, cHaving, ;
                      cOrder, cLimit, lWithRoll )   
                      
   METHOD CheckError( nError, cExtra )  INLINE ::oServer:CheckError( nError, cExtra )
                               /*Compatibility with CheckError from TDolphinSrv*/

   METHOD Delete( lAll )       /*Delete current record active*/
#ifdef __WIN__
   METHOD Export( nType, cFieldName, aColumns, aPictures )   ;
                INLINE TDolphinExport():New( nType, Self, cFieldName, aColumns, aPictures )
#endif __WIN__
   METHOD Eof()        INLINE ::lEof  
                      
   METHOD FCount()     INLINE    ::nFCount
                        /*returns the number of fields in the query, compatibility with dbf*/

   METHOD FieldLen( cnField )
   METHOD FieldDec( cnField )

   METHOD FieldName( nNum )     /*returns the name of the specified field as a character string.*/
   METHOD FieldPos( cFieldName )
                                /*returns the position of the specified field*/
   METHOD FieldGet( cnField )   /*returns the value of the specified field*/
   METHOD FieldType( cnField )  /*returns the field type of the specified field*/
   METHOD FieldMySqlType( cnField ) 
                                /*returns the MySql field type of the specified field*/
                                
   METHOD FieldPut( cnField, uValue )
                         /*Set the value of a field variable using the ordinal position of the field.
                                 returns the value assigned to the designated field.*/  
                                  
   METHOD FieldToNum( cnField ) HIDDEN                                                            

   METHOD FillArray( bOnFillArray, aColumns ) /*Fill and return a array with all query information*/
   METHOD FillHRow( lEmpty )          /*Fill (or not) and return a Hash with current record selected*/

   METHOD FirstPage()   INLINE ::PrevPage( ::nCurrentPage - 1 )
                                /*Go to first page in pagination*/
   METHOD Find( aValues, aFields, nStart, nEnd, lRefresh )                                
   
   METHOD GetBlankRow()                       
   METHOD GetRow( nRow )        /*Fill aRow and Hash with current data row selected*/
   METHOD GoTo( nRow ) INLINE   ::GetRow( nRow )
                                /*Goto specific Row (RecNo) and fill aRow/Hash*/
                                
   METHOD GetFieldsModified()   /*Return a Array with fields modified*/                             

   METHOD GetRowObj( nRow )     /*Return TDolphinRow Object*/
                                
   METHOD GoBottom()   INLINE   ::GetRow( ::nRecCount  ) 
                                /*Goto BOTTOM of Query and fill aRow/Hash*/
                                
   METHOD GoTop()      INLINE   ::GetRow( 1 )
                                /*Goto TOP of Query and fill aRow/Hash*/
                                
   METHOD IsEqual( nIdx )                                

   METHOD IsSingleTable() INLINE Len( ::aTables ) == 1  
   METHOD IsCommand()     INLINE ( ::IsSingleTable() .AND. Len( ::aColumns ) == 0 ) .OR. ;
                                 ( Len( ::aTables ) < 1 .AND. Len( ::aColumns ) >= 1 )


   METHOD LastPage() INLINE ::NextPage( ::nMaxPages - ::nCurrentPage )
                               /*Go to Last page in pagination*/

                                  
   METHOD LastRec()    INLINE     ::nRecCount
                   /*returns the number of rows in the current query, compatibility with dbf*/

   METHOD LoadQuery()          /*Load and fill current query*/
   METHOD LoadNextQuery( lBuildData ) 
                               /*Load next result with multiple statement*/
   
   METHOD Locate( aValues, aFields, nStart, nEnd, lSoft, lRefresh )

   METHOD MakePrimaryKeyWhere() 
                               /*Build Make Primary key if exist*/
   
   METHOD GoToPage( nPage ) INLINE If( nPage > ::nCurrentPage, ;
                            ::NextPage( nPage - ::nCurrentPage ), ;
                            If( nPage < ::nCurrentPage, ;
                            ::PrevPage( ::nCurrentPage - nPage ), ) )
       
   METHOD NextPage( nSkip )    /* Go to next page avalaible with pagination active */
   
   METHOD PrevPage( nSkip )    /* Go to previous page avalaible with pagination active */

   METHOD RecNo()      INLINE    ::nRecNo
                               /*returns the identity found at the position of the row pointer.*/
                               
   METHOD RecCount()   INLINE ::LastRec()
                               /*Compatibility with TMysql*/
                               
   METHOD Refresh( lBuildData )    
   
   METHOD Save()               /*Save current data*/
                 
   METHOD Seek( cSeek, cnField, nStart, nEnd, lSoft ) 
                               /*Move to the record having the specified 
                                 cSeek value, in selected field
                                 from nStart to nEnd with SoftSeek*/
                 
   METHOD SetData( nNum, uValue ) HIDDEN
                               /*set value into array or hash*/
   
   METHOD SetNewFilter( nType, cFilter, lRefresh )
   METHOD SetWhere( cWhere, lRefresh )   INLINE ::SetNewFilter( SET_WHERE, cWhere, lRefresh )
   METHOD SetGroup( cGroup, lRefresh )   INLINE ::SetNewFilter( SET_GROUP, cGroup, lRefresh )
   METHOD SetHaving( cHaving, lRefresh ) INLINE ::SetNewFilter( SET_HAVING, cHaving, lRefresh )
   METHOD SetOrder( cOrder, lRefresh )   INLINE ::SetNewFilter( SET_ORDER, cOrder, lRefresh )
   METHOD SetLimit( cLimit, lRefresh )   INLINE ::SetNewFilter( SET_LIMIT, cLimit, lRefresh )
   
   METHOD SetPages( nLimit )   /*Activate pagination and Set total rows by page*/
  
   METHOD Skip( nRecords )
   
   METHOD VerifyValue( nIdx, cField ) //HIDDEN
   
   METHOD Undo( cnField )

   METHOD Zap() INLINE ::Delete( .T. )
                               /*Delete all record in table*/
   
   ERROR HANDLER ONERROR()   

ENDCLASS


//Main class to manager exports
CLASS TDolphinExport

    DATA oQuery        /* Query object */
    DATA aColumns      /* Columns seelct to fill file */
    DATA aPictures     /* Columns's Picture */

    DATA cFileName     /* File name */
    DATA hFile
    DATA lAddHeader

    DATA nType         /* Export type */
    DATA bOnRow        /* codeblock to evaluate row by row */
    DATA bOnStart      /* codeblock to evaluate at the begin process */
    DATA bOnEnd        /* codeblock to evaluate at the end process */
    DATA oExport       /* Export Objet, is direftent by type */
    DATA oMain         /* Self */


    METHOD New( nType, oQuery, aColumns, aPictures  )

    METHOD Start() INLINE ::oExport:Export()
    METHOD Write( cLine, lNoCRLF )
    METHOD Close()  INLINE FClose( ::hFile )

    ERROR HANDLER ONERROR()


ENDCLASS

Activar conexión remota MySQL

Este es un tema muy amplio y con múltiples soluciones debido a la gran variedad de distribuciones de MySQL, proveedores de hosting, configuraciones exclusivas, etc.

En esta oportunidad describiré como habilité la conexión en una red local que tiene un servidor MySQL incluido en la distribución XAMPP, básicamente ingresé a localhost/phpMyAdmin y configuré un nuevo usuario con los privilegios necesarios para acceder a la base de datos.

2015-05-11_112047
2015-05-11_112159
El Nuevo usuario exoasesor del PC con IP 192.168.20.2  tiene amplios privilegios y puede acceder a la base de datos del servidor MySQL con IP 192.168.20.1

2015-05-11_111308
Realice pruebas desde una red externa y también se logra conectar con éxito.

Para conectar de forma remota a una base de datos MySQL en un servidor de hosting lo hice como indico a continuación:

Ingresé al cPanel ejemplo: www.miweb.com/cpanel

2015-05-11_102717
Nos dirigimos a la sección Base de datos

2015-05-11_101831

En MySQL Remoto (Si el servicio contratado permite esta opción, de lo contrario no es posible por el momento)

2015-05-11_102552Podemos utilizar % para que cualquier IP tenga acceso remoto, pero lo recomendable por razones de seguridad es incluir la IP de los equipos que consideremos habilitados para acceder a la base de datos.

Es Importante crear los usuarios y sus privilegios en la opción MySQL Bases de Datos:

2015-05-11_101831
2015-05-11_102307
Asignamos el usuario a la base de datos, con estos pasos estamos listos para conectar en forma remota.

Guía de Inicio HDroidGUI

Based in HDROIDGUI SETUP TUTORIAL
http://www.hmgforum.com/viewtopic.php?f=35&t=4229&hilit=hdroidgui

1) Download Java Development Kit (JDK) version 7 for Windows (if you have other version installed, please uninstall first):

http://www.oracle.com/technetwork/es/java/javase/downloads/jdk7-downloads-1880260.html 

2015-03-27_110459

2015-03-27_110601

2) Run it and accept suggested install location (at the date of this post, it is: C:\Program Files\java\jdk1.7.0_75. It will change according version number) .

3) Go to: https://developer.android.com/sdk/index.html (‘SDK Tools Only’ section, near the end of the page) and download Windows version.

2015-03-27_111754

2015-03-27_112228

 

4) Create the folder tree: c:\Android\sdk and install Android SDK there.

2015-03-27_113035 2015-03-27_113115 2015-03-27_113151 2015-03-27_113232

 

5) Run C:\Android\sdk\SDK Manager.exe and follow these directions found in android.com:

2015-03-27_113744

 

As a minimum when setting up the Android SDK, you should download the latest tools and Android platform:

Open the Tools directory and select:
Android SDK Tools
Android SDK Platform-tools
Android SDK Build-tools (highest version)

Open the first Android X.X folder (the latest version) and select:
SDK Platform
A system image for the emulator, such as
ARM EABI v7a System Image

6) Go to https://developer.android.com/tools/sdk/ndk/index.html and download Android NDK for Windows release 10d (at the time of this post) and extract in C:\Android\android-ndk-r10d

2015-03-27_114429
7) Go to https://github.com/alkresin/HDroidGUI and click on ‘Download ZIP’. Unzip on C:\Android\projects\HDroidGUI

2015-03-27_115050

 

8) Edit the file C:\Android\projects\HDroidGUI\setenv.bat and change paths according to the location of the tools you’ve installed. With the versions installed at the time of this post, it should look as this:

@set ANDROID_HOME=c:\Android\sdk
@set BUILD_TOOLS=%ANDROID_HOME%\build-tools\21.1.2
@set NDK_HOME=c:\Android\android-ndk-r10d
@set JAVA_HOME=c:\Program Files (x86)\Java\jdk1.7.0_75

@set ADB=%ANDROID_HOME%\platform-tools\adb.exe
@set ANDROID_JAR=%ANDROID_HOME%/platforms/android-21/android.jar

@set HRB_BIN=c:\hb32\bin
@set HRB_INC=c:\hb32\include

c:\Android\projects\HDroidGUI\comp.bat

@call setenv

call "%JAVA_HOME%/bin/javac" -d libs -cp %ANDROID_JAR% -sourcepath src src/su/harbour/hDroidGUI/*.java
@if errorlevel 1 goto end

@rem jar cvf hdroidgui.jar -C libs .
:end
@pause

Tips:

– Please, be careful: Path names changes with tools versions!
– JAVA_HOME could be different according your Windows version and language. Please, check it.
– I have Harbour installed at c:\hb32. If that is not your path, please change it as needed. If you do not have Harbour installed, please check it.

Download from:
http://sourceforge.net/projects/harbour-project/files/latest/download?source=files
and unzip at c:\hb32

9) Build HDROIDGUI: C:\Android\projects\HDroidGUI\BuildAll.bat

2015-03-27_115910

 

10) Now, we will download a sample project called HDCALCUL. Go to https://github.com/alkresin/hdcalcul and click on ‘Download ZIP’. Unzip on c:\Android\projects\hdcalcul

View c:\Android\projects\hdcalcul\setenv.bat

@set HDROIDGUI=\android\projects\hdroidgui
@call %HDROIDGUI%/setenv

@set APPNAME=hdcalcul
@set PACKAGE=su.harbour.hdcalcul
@set PACKAGE_PATH=su\harbour\hdcalcul
@set MAIN_CLASS=MainActivity
@set DEV_HOME=%CD%

11) build the .apk running: c:\Android\projects\hdcalcul\build.bat. The .apk will be generated at c:\Android\projects\hdcalcul\bin.

@call setenv
@call clear

@%HRB_BIN%\harbour src\main.prg /q /i%HDROIDGUI%\src\include /i%HRB_INC% /ojni\

@if errorlevel 1 goto end

@set NDK_LIBS_OUT=lib

@set SRC_FILES=main.c
%NDK_HOME%\prebuilt\windows\bin\make.exe -f %NDK_HOME%/build/core/build-local.mk %* >a1.out 2>a2.out

@if exist lib\armeabi\libh4droid.so goto comp
@echo Errors while compiling C sources
@goto end

:comp
call %BUILD_TOOLS%/aapt.exe package -f -m -S res -J src -M AndroidManifest.xml -I %ANDROID_JAR%

@if errorlevel 1 goto end

@rem compile, convert class dex

@rem call %JAVA_HOME%/bin/javac -d obj -cp %ANDROID_JAR%;%HDROIDGUI%\hdroidgui.jar -sourcepath src src/%PACKAGE_PATH%/*.java
call "%JAVA_HOME%/bin/javac" -d obj -cp %ANDROID_JAR%;%HDROIDGUI%\libs -sourcepath src src/%PACKAGE_PATH%/*.java

@if errorlevel 1 goto end

call %BUILD_TOOLS%/dx.bat --dex --output=bin/classes.dex obj %HDROIDGUI%\libs

@if errorlevel 1 goto end

@rem create APK
call %BUILD_TOOLS%/aapt.exe package -f -M AndroidManifest.xml -S res -I %ANDROID_JAR% -F bin/%APPNAME%.unsigned.apk bin

call %BUILD_TOOLS%/aapt.exe add %DEV_HOME%/bin/%APPNAME%.unsigned.apk lib/armeabi/libharbour.so

@if errorlevel 1 goto end

call %BUILD_TOOLS%/aapt.exe add %DEV_HOME%/bin/%APPNAME%.unsigned.apk lib/armeabi/libh4droid.so

@rem sign APK
call "%JAVA_HOME%/bin/keytool" -genkey -v -keystore myrelease.keystore -alias key2 -keyalg RSA -keysize 2048 -validity 10000 -storepass calcpass -keypass calcpass -dname "CN=Alex K, O=Harbour, C=RU"

call "%JAVA_HOME%/bin/jarsigner" -sigalg SHA1withRSA -digestalg SHA1 -keystore myrelease.keystore -storepass calcpass -keypass calcpass -signedjar bin/%APPNAME%.signed.apk bin/%APPNAME%.unsigned.apk key2
%BUILD_TOOLS%/zipalign -v 4 bin/%APPNAME%.signed.apk bin/%APPNAME%.apk

:end
@pause

12) To test it, you only must to copy the .apk generated file to an Android device
and run it.

13) In my case I have synchronized the smarphone and notebook with the cloud, so I copy the .apk file from the notebook folder and automatically updated on the smartphone, same with screenshots of the smartphone are immediately in the notebook 🙂

Screenshot_2015-03-25-pag1

Proyecto de “Desarrollo de Software”

  1. La solicitud del usuario
  2. Lo que entendió el líder del proyecto
  3. El diseño del analista de sistemas
  4. El enfoque del programador
  5. Las recomendaciones del consultor externo
  6. La documentación del proyecto
  7. La implantación en producción
  8. El presupuesto del proyecto
  9. El soporte operativo
  10. Lo que el usuario realmente necesita

El Proceso se indica en la siguiente imagen (Fuente:  Objeto Persistente )

Como compilar en Harbour

/*

* $Id: howtobld.txt 9191 2008-08-19 13:11:22Z vszakats $

*/

In the last phase of install process if bash shell is available in the

system then few bash scripts are created to make compiling and linking

with Harbour a little easier. There are compiler and linker wrappers

called “hbcc“, “hbcmp“, “hblnk” and “hbmk“.

hbcc” is a wrapper to the C compiler only. It sets all flags

and paths necessary to compile .c files which include Harbour header

files. The result of its work is an object file.

Use “hbcmp” exactly as you would use the harbour compiler itself.

The main difference with hbcmp is that it results in an object file,

not a C file that needs compiling down to an object. hbcmp also

ensures that the harbour include directory is seen by the harbour compiler.

hblnk” simply takes a list of object files and links them together

with the harbour virtual machine and run-time library to produce an

executable. The executable will be given the basename of the first object

file if not directly set by the “-o” command line switch.

hbmk” tries to produce an executable from your .prg file. It’s a simple

equivalent of cl.bat from the CA-Clipper distribution.

All these scripts accept command line switches:

-o<outputfilename>    # output file name

-static                          # link with static Harbour libs

-fullstatic                     # link with all static libs

-shared                        # link with shared libs (default)

-mt                              # link with multi-thread libs

-gt<hbgt>                   # link with <hbgt> GT driver, can be repeated to

# link with more GTs. The first one will be

# the default at runtime

-xbgtk                          # link with xbgtk library (xBase GTK+ interface)

-hwgui                          # link with HWGUI library (GTK+ interface)

-l<libname>                 # link with <libname> library

-L<libpath>                  # additional path to search for libraries

-fmstat                         # link with the memory statistics lib

-nofmstat                     # do not link with the memory statistics lib (default)

-[no]strip                      # strip (no strip) binaries

-main=<main_func>     # set the name of main program function/procedure.

# if not set then ‘MAIN’ is used or if it doesn’t

# exist the name of first public function/procedure

# in first linked object module (link)

Link options work only with “hblnk” and “hbmk” and have no effect

in “hbcc” and “hbcmp“.

Other options are passed to Harbour/C compiler/linker.

An example compile/link session looks like:

[email protected]:~/tmp$ cat foo.prg

function main()

? “Hello, World!”

return nil

[email protected]:~/tmp$ hbcmp foo

Harbour Compiler Alpha build 46.2 (Flex)

Copyright 1999-2006, http://www.harbour-project.org/

Compiling ‘foo.prg’…

Lines 5, Functions/Procedures 2

Generating C source output to ‘foo.c’… Done.

[email protected]:~/tmp$ hblnk foo.o

[email protected]:~/tmp$ strip foo

[email protected]:~/tmp$ ls -l foo

-rwxrwxr-x    1 druzus   druzus       3824 maj 17 02:46 foo

or using hbmk only:

[email protected]:~/tmp$ cat foo.prg

function main()

? “Hello, World!”

return nil

[email protected]:~/tmp$ hbmk foo

Harbour Compiler Alpha build 46.2 (Flex)

Copyright 1999-2006, http://www.harbour-project.org/

Compiling ‘foo.prg’…

Lines 5, Functions/Procedures 2

Generating C source output to ‘foo.c’… Done.

[email protected]:~/tmp$ ls -l foo

-rwxrwxr-x    1 druzus   druzus       3824 maj 17 02:46 foo

You will find additional wonderful tools: /usr/bin/hbrun

You can run clipper/xbase compatible source files with it

if you only put in their first line:

#!/usr/bin/hbrun

For example:

[email protected]:~/tmp$ cat foo.prg

#!/usr/bin/hbrun

function main()

? “Hello, World!, This is a script !!! “

?

return nil

[email protected]:~/tmp$ chmod +x foo.prg

[email protected]:~/tmp$ ./foo.prg

Hello, World!, This is a script !!!

[email protected]:~/tmp$

I hope you will find this information useful,

Przemyslaw Czerpak (druzus/at/priv.onet.pl)

A propósito de GT driver

How to create such base GUI GT driver?

See as example GTGUI in source/rtl/gtgui/gtgui.c

It supports only TONE and CLIPBOARD operations.

GUI libraries can use it or create other GT driver inheriting from

this one.

NOTE: source/rtl/gtgui/gtdef.c is a hack which overloads the default

Harbour build time GT driver and should not be replicated.

  • gtnul – base GT driver from which each other inherits.

it gives screen buffer functionality but does not

produce any screen output from disp*() commands

Only outStd()/outErr() are supported.

It’s present on all platforms and i always linked.

  • gtcgi – very simple GT driver which does not make any output

formatting and simply send it as to stdout.

Supported by all platforms.

  • gtstd – it uses stdout output but tries to support full screen output but

without collor support and cursor shape. It format text to number

of row and columns if is able to detect these values on given

platform.

Supported by all platforms.

  • gtpca – It’s PCANSI terminal GT – it works in similar way to ANSI GT

driver in Clipper though keyboard input is not fully supported.

Now GTTRM can make all GTPCA job and much more.

Supported by all platforms.

  • gtdos – GT driver for DOS – it uses BIOS and direct hardware screen output

so it’s very similar to Clipper one – in practice due to the same

environment you will noticed that all small details of Clipper

GT drivers are replicated here. You can think about it like 100%

Clipper compatible.

Supported only by DOS builds.

  • gtos2 – GT driver for OS2 – It’s sth like GTWIN but for other OS.

Supported only by OS2 builds.

  • gtwin – GT driver for MS-Windows console window.

Supported only by MS-WINDOWS (W95 or higher) builds.

  • gtcrs – GT driver for platforms which supports curses or compatible

(ncurses) library – in practice POSIX systems.

Supported by POSIX systems (mostly different *nixes)

  • gtsln – GT driver for platforms which supports slang library.

It’s like GTCRS but instead of CURSES it uses SLANG.

Supported by POSIX systems (mostly different *nixes).

It supports Unicode input/output if compiled with slang

version which also supports it.

  • gttrm – it’s like GTCRS and GTSLN but it does not use any external

terminal library like SLANG or CURSES and it does not use

any external database to extract terminal capabilities so

it can be compiled on any POSIX system – I’ll add support

also for DOS/Windows in the future. It should automatically

detect UTF-8 terminal mode and switch internally to Unicode

mode if necessary. In theory is less functional then GTCRS

and GTSLN because I hard coded escape sequences only for few

terminals but because I added support also for some non

standard terminal extensions and I’m using very limited set

of output sequences then it usually works better then GTCRS

and GTSLN.

Supported by POSIX systems (mostly different *nixes)

  • gtwvt – GT driver for MS-Windows. It creates its own GUI window

instead of using MS-console window. It allows to change

font, window size, etc.

Supported only by MS-WINDOWS (W95 or higher) builds.

is a pure console implementation of traditional Clipper terminal

taking Windows API as its base console IO protocols. Its OI are

rendered in a Windows window and hence all of MSDN is available

for use with GTWVT.

To let the memory refreshed, I must remind

everybody that it is a superb work of Peter Rees contributed

to xHarbour on 22nd December 2003.

  • GTWVG – GUI emulation of GTWVT. It implements itself on top of GTWVT.

GTWVG ( WVTGUI in xHarbour ) offers functions and classes to

present a console application look like a windows one.

It renderes GUI elements on top of Clipper elements

( GETS, BROWSERS, BOXES, LINES ) which makes them

feel like a Windows element. GTWVG can be used with existing

code just adding some more code but without sacrificing or

modifying old one.

Also all IO commands can be rendered on top of the GUI elements

  • gtxwc – GT driver for X-Window. It’s like GTWVT but for nixes.

Additionaly it has set of predefined vector characters

(box and arrowd drawing characters) which can be used

instead of the one defined in font. It means that you

will have all boxes and arrows you know from DOS ans CP437

even if you chose font which does not have them. Additionally

it support some simple graphic output. See tests/gfx.prg

for simple program which demonstrates it.

Supported by POSIX systems (mostly different *nixes)

  • gtalleg– GT driver which uses alegro library for input/output.

It’s also GUI driver which support HB_GFX*() drawing.

Multi platform, works on all platforms for which allegro

library has been ported: MS-Win, DOS, VESA, X11, FB, SDL, …

  • gtgui – pseudo GT driver which adds to GTNUL Clipboard and Tone

functionality. If you are using some MS-Windows GUI library

and you still want to use TONE() function or GTI_CLIPBOARD

actions then link this GT driver with your application.

If you do not want to use TONE() or GTI_CLIPBOARD then

do not link it – it will be only waste of memory.

  • gtctw – GT driver which adds CT3 Window functionality to any other

GT driver from which it inherits. It’s activated automatically

when you execute first W*() function.

In similar way in Harbour it’s possible to add support for

GTWVW inheriting from GTWVT.

Some of the GT drivers support additional functionality with hb_gtInfo()

interface.

See include/hbgtinfo.ch for different actions.

Best regards,

Przemek

Controlando la consola

How to get rid of unwanted console in Windows GUI applications

By Przemyslaw Czerpak (druzus/at/priv.onet.pl)

Do not use GTWIN 🙂

GTWIN is a driver for users who wants to write GUI applications and

should give them all possible features. GUI libraries do not have to

use GT drivers if they don’t need any GT functionality, or if they

don’t want to give users support for standard Clipper/Harbour functions

which operate on GT resources.

Harbour application can work without any GT driver. In such case all

functions which operates on GT resources are redirected to meta GT

driver (GTNUL) which is part of RTL library. This driver makes all

operations on memory buffer and only OUTSTD()/OUTERR() output is sent

outside. All GT drivers inherits from GTNUL or from other GTs but in

the inheritance chain the first GT driver is always GTNUL.

Because there is no hard coded bindings between core code and other GT

drivers, then by default only GTNUL will be linked. So if you will want

to use some real GT you will have to add to your code:

REQUEST HB_GT_<name>

Setting the default GT driver is done exactly the same as setting the

default RDD. In RDD it request DBFNTX by default. It is done inside a

module with RDDSYS() symbol, and core code contains:

REQUEST RDDSYS

Something like that is also done by Clipper. If you add to your code

RDDSYS symbol, then the default RDD will not be linked because your

RDDSYS will overload the default one (of course if it will be linked

before the one in core code). So it’s enough to write something like:

ANNOUNCE RDDSYS

or:

PROC RDDSYS; RETURN

Both gives the same effect, and default RDD (DBFNTX) will not be linked.

Exactly the same I’ve done in GT subsystem. HB_GTSYS() makes exactly

the same job as RDDSYS() but for GT. This symbol is requested by core

code and in the module where it is defined it request default build GT

driver or if it’s not set then default GT driver for given platform.

For Windows it looks like:

ANNOUNCE HB_GTSYS

REQUEST HB_GT_WIN

This causes that normal console applications do not have to explicitly

request GT driver and the one set in the module with HB_GTSYS is always

linked. If you do not want to link the GT driver then you have to make

the same as for RDD and add to your code:

ANNOUNCE HB_GTSYS

or:

PROC HB_GTSYS; RETURN

In such case your final application will not have any GT driver. If you

want to use GTNUL as base, you should add:

REQUEST HB_GT_NUL

Though IMHO this request should be part of GUI library core code. You

can link with your application more then one GT driver. It’s enough

that you add more lines with:

REQUEST HB_GT_<name>

For example, compile this code:

/*** t.prg ***/

REQUEST HB_GT_WIN

REQUEST HB_GT_WVT

PROCEDURE MAIN()

? HB_GTVERSION(), HB_GTVERSION(1)

TONE( 200, 3 )

TONE( 300, 3 )

TONE( 500, 3 )

INKEY( 0 )

RETURN

and link it as Windows GUI application. Then simply execute:

t //GTWIN

and:

t //GTWVT

Most of Windows linkers execute startup initialization code in the

order of linked modules, so the first linked GT driver will be the

default one. But you can control it also from your application, by

requesting HB_GT_<name>_DEFAULT symbol (I do not like this name because

it cannot be used with 10 character symbols so I’m ready for any other

positions). For example, if you add to your code:

REQUEST HB_GT_NUL_DEFAULT

Then GTNUL will be the default GT driver, and even if you would not

disable GTWIN and link it with your application, GTWIN will not be

activated but GTNUL.

It could be intentional, because if your application is linked with more

GTs, then you can also set the default one when you start your

application using //GT<name> switch, or HB_GT environment variable. So

you can create GUI application which will set the default GT driver to

NUL and will not activate GTWIN, and when you’ll want to enable debug

messages, you simply run:

myprog //GTWIN

and debug messages will use the GTWIN console window. You can think of

other situations when it could be useful to have full functional GT

driver in GUI application. You can even create mixed GUI/CUI code in one

program.

And finally, the TONE() function problem.

Low level TONE code is part of GT driver. In the past, GUI libraries in

Windows were linked the whole GTWIN driver and only TONE were used. It

was possible because someone blocked GTWIN to work with application

linked as Windows GUI programs. Now, GTWIN can be used with any

applications, and I do not want reduce its functionality. So GUI

libraries which needs TONE should have their own GT driver which will

support it. Now, such GT driver can also give much more features for

final users, because it allow to integrate GUI library with standard

Clipper screen functions.