Return value from modal window

General Help regarding HMG, Compilation, Linking, Samples

Moderator: Rathinagiri

User avatar
sudip
Posts: 1454
Joined: Sat Mar 07, 2009 11:52 am
Location: Kolkata, WB, India

Return value from modal window

Post by sudip »

Hi All,

How can I return value from a modal window?

I want to write a general purpose search / find / browse function which will create a modal window with Browse/Grid and return record number or primary key of the table searched.

TIA.

With best regards.

Sudip
With best regards,
Sudip
User avatar
Rathinagiri
Posts: 5471
Joined: Tue Jul 29, 2008 6:30 pm
DBs Used: MariaDB, SQLite, SQLCipher and MySQL
Location: Sivakasi, India
Contact:

Re: Return value from modal window

Post by Rathinagiri »

Either we can use a public/private variable or update a hidden/shown textbox control in the calling window. After doing that we can release the modal window.
East or West HMG is the Best.
South or North HMG is worth.
...the possibilities are endless.
User avatar
sudip
Posts: 1454
Joined: Sat Mar 07, 2009 11:52 am
Location: Kolkata, WB, India

Re: Return value from modal window

Post by sudip »

rathinagiri wrote:Either we can use a public/private variable or update a hidden/shown textbox control in the calling window. After doing that we can release the modal window.
Thanks a lot. :)
Regards.
Sudip
With best regards,
Sudip
User avatar
dhaine_adp
Posts: 457
Joined: Wed Aug 06, 2008 12:22 pm
Location: Manila, Philippines

Re: Return value from modal window

Post by dhaine_adp »

Or in a much lesser simplicity, the way I do it is I passed a local variable to the function that contains the modal window. However I used to pass the local variable by reference and not by value which means I used the "@" symbol.

Example:

function main()

LOCAL cKey := ""

FindKey( @cKey )

RETURN

function FindKey( cKey )

** here, I will pass the variable by reference as long as I need it and it must be set before terminating the modal window.


Well there are more than one way to accomplish things but it is purely depends on our own preference. Like the clipper old days, I am trying to minimize the used of public and private variable.

Another thing is that when things becomes redundant, I used HBClass when it is possible.

Regards,

Danny
Regards,

Danny
Manila, Philippines
User avatar
Rathinagiri
Posts: 5471
Joined: Tue Jul 29, 2008 6:30 pm
DBs Used: MariaDB, SQLite, SQLCipher and MySQL
Location: Sivakasi, India
Contact:

Re: Return value from modal window

Post by Rathinagiri »

Nice explanation. Thanks a lot Danny.
East or West HMG is the Best.
South or North HMG is worth.
...the possibilities are endless.
User avatar
sudip
Posts: 1454
Joined: Sat Mar 07, 2009 11:52 am
Location: Kolkata, WB, India

Re: Return value from modal window

Post by sudip »

Hello Danny,
dhaine_adp wrote:Or in a much lesser simplicity, the way I do it is I passed a local variable to the function that contains the modal window. However I used to pass the local variable by reference and not by value which means I used the "@" symbol.
...
...
Well there are more than one way to accomplish things but it is purely depends on our own preference. Like the clipper old days, I am trying to minimize the used of public and private variable.

Another thing is that when things becomes redundant, I used HBClass when it is possible.
Thanks a lot. I like it. And I shall do with call by ref.
BTW, I never used HBClass. Can we do OOP with it? If yes, thank you again :) This will solve data redundancy.
With best regards.
Sudip
With best regards,
Sudip
User avatar
dhaine_adp
Posts: 457
Joined: Wed Aug 06, 2008 12:22 pm
Location: Manila, Philippines

Re: Return value from modal window

Post by dhaine_adp »

Hi Sudip,

Here is my modal window class that picks a record that I wish to share as an example. This is my actual code and it goes like this:

Code: Select all

/************************************************************************************ 
 * Program Name....: RecordPicker.Prg
 * Purpose ........: ValleyLiBs® Record Picker Class
 *
 * Syntax..........: Harbour + Harbour OOP + HMG MiniGUI + MinGW32
 *
 * Author..........: Danny A. del Pilar
 * E-mail..........: dhaine_adp@yahoo.com
 * Note............: Copyright © 2008 Danny A. del Pilar, All Rights Reserved.
 *                   La DALE-Aid® Creative Solutions, Philippines
 *
 * Date Created....: November 28, 2008 09:17:15 PM, Abqaiq, K.S.A.
 * Last Updated....:
 *  
 * 
 * Revision History: 
 * -----------------
 * 
 * December 28, 2008 05:43:04 PM, My Room, Abqaiq, K.S.A.
 *    METHOD PickStatus() has been added to check if the user selects a record or not.
 *    This is an enhancement functionality added to the CLASS itself.
 * 
 * December 08, 2008 04:25:36 AM, Abqaiq, K.S.A.
 *    All testing is done, production release has been achieved. This OOP Class is working
 *    as expected. No further work is required unless I decided to change it's behaviour or
 *    additional functionality. Safe to add in ValleyLiBs® Production Code Library
 * 
 *    Additional Method and instance variable is added to handle the return value.
 *    Added Methods: BtnOKSelection() in the window events btnOK and ON ENTER in browse
 *    
 *    Set focus to browse after pressing enter in quick search is added.
 *
 * December 02, 2008 12:38:36 AM, Abqaiq, K.S.A.
 *    All work here is suspended until the changes made to MakeFile.Prg is completed.
 *       Account title is required to complete the testing here. See comments in ChartAct.Prg
 *
 * November 28, 2008 09:17:15 PM, Abqaiq, K.S.A.
 *    A total revision of RecordPicker() I wrote on December 19, 2005 09:48:29 PM, Najran, KSA
 *    
 *---------------------------------------------------------------------------------------    
 * Notes: 
 *    
 *    
 *****************************************************************************************
*/


#include "minigui.ch"
#include "inkey.ch"
#include "common.ch"
#include "dale-aid.ch"
#include "hbclass.ch"



//****************************************************************************************//
CLASS RecordPicker                                                                        //
//****************************************************************************************//
// NOTES: The constructor expected all the parameters, otherwise NIL or an array of NIL   //
//        element is initiliazed as default values of the instance variables. In this     //
//        case, manual initialization of instance variables is required prior to the call //
//        to display the Picker Window.                                                   //
//                                                                                        //
//        Well, this can also be accomplished by not making this routine as a Class but   //
//        designing it in this way makes it more easy to extend instead of overloading the//
//        function of PARAMETERS and inherrited Private Variables. This would violate my  //
//        program design principles and coding standard. Once I released the routine      //
//        into production it should be self reliant, stable and would not cause           //
//        any interference to another routine. It should be rock solid in all angle. So   //
//        that I could forget it until the day that I wish to modify this!                //
//                                                                                        //
//        Windows and Form objects are all internal to this class                         //
//                                                                                        //
//        (1) If scoping is required in searching or displaying values all the instance   //
//            variables are required to be set before calling the method                  //
//            REcordPicker():Activate()                                                   //
//                                                                                        //
//        (2) If the instance variable aComboPick_ length is 1, it is not required to     //
//            define aComboBlock_, otherwise it should be defined.                        //
//                                                                                        //
//        (3) There are undocumented features in this RecordPicker Class that I never     //
//            documented out of PURE LAZINESS.                                            //
//                                                                                        //
//        Anyway, this saves the day instead of writing two or more custom versions of    //
//        RecordPicker()                                                                  //
//                                                                                        //
// November 29, 2008 07:30:40 PM - Danny                                                  //
//****************************************************************************************//


   EXPORTED:
   DATA lRecScope                // programmatically set to logical TRUE or FALSE
                                 //    it controls the behaviour of seek when ORDSCOPE() is active
   DATA cScopeTop                // when lRecScope is TRUE, this must be set programmatically,
   DATA cScopeBottom             // this intance variable
   DATA cSeekPrefix              // and this variable too. 
   DATA aSelection_              // Unfortunately this one too. Field list to be return upon exit
                                 // by default, FIELDGET(1) will be returned. In program call, it should be
                                 // set to array so all the desired field list can be returned
      
   DATA cIndexFile               // Index file
   DATA cOrderBag                // default order bag use in search or quick search
   
   DATA aComboPick_              // Drop down combobox pick list, controls table index, works in tandem with
                                 // with aComboBlock_. 
   DATA aComboBlock_             // code blocks to be executed when a selection is selected from the combobox pick list
                                 // this is an external function to provide extension to make it more generic
   DATA cWorkarea                // default browse workarea
   DATA aBrwFields_              // fields to be display in browse
   DATA aBrwColLabels_           // browse column labels
   DATA aColWidths_              // browse column widths
   DATA cWndTitle                // Record Picker Window title
   
   DATA bCustomSearch            // code block to call external UDF for seeking records
   
   DATA cImageHome               // Image folder
   
   METHOD New( aComboPick_, aComboBlock_, cWorkarea, aBrwFields_, aBrwColLabels_, aColWidths_, cWndTitle ) CONSTRUCTOR
   METHOD CustomSearch() INLINE EVAL( ::bCustomSearch )
   METHOD Activate()             // activate the record picker window
   METHOD Destroy()              // re-initialized all the instance variables
   METHOD PickStatus( aVar_ )    // verify if user has picked or cancelled the action

   HIDDEN:
   METHOD ComboboxChange()       // Action when the combox value is change
   METHOD QSearch()              // Perform relative seek and place the record pointer to the next higher index key
   METHOD BtnOKSelection()       // set the return value and release window

   END CLASS




****************************************************************************************************************************
METHOD New( aComboPick_, aComboBlock_, cWorkarea, aBrwFields_, aBrwColLabels_, aColWidths_, cWndTitle ) CLASS RecordPicker

   DEFAULT aComboPick_    TO {"1"}
   DEFAULT aComboBlock_   TO {}
   DEFAULT cWorkarea      TO NIL
   DEFAULT aBrwFields_    TO {}
   DEFAULT aBrwColLabels_ TO {}
   DEFAULT aColWidths_    TO {}
   DEFAULT cWndTitle      TO "Please Select Below"
   
   **************************************************
   ** begin initializing all the intance variables **
   **************************************************   
   
   ::lRecScope      := FALSE
   ::cScopeTop      := ""
   ::cScopeBottom   := ""
   ::cSeekPrefix    := ""

   ::cIndexFile     := ""
   ::cOrderBag      := ""

   ::aComboPick_    := aComboPick_   
   ::aComboBlock_   := aComboBlock_  
   ::cWorkarea      := cWorkarea     
   ::aBrwFields_    := aBrwFields_   
   ::aBrwColLabels_ := aBrwColLabels_ 
   ::aColWidths_    := aColWidths_   
   ::cWndTitle      := cWndTitle     
   
   ::bCustomSearch  := ""          // code block to call external UDF for seeking records
   
   ::cImageHome     := ""
   
   DO CASE
      CASE VALTYPE( ::aSelection_ ) == "U"
         ::aSelection_ := { 1 }
         
      CASE VALTYPE( ::aSelection_ ) == "N"
         ::aSelection_ := { ::aSelection_ }
         
      CASE VALTYPE( ::aSelection_ ) == "C"
         ::aSelection_ := { VAL( ::aSelection_ ) }
   ENDCASE
   
   RETURN Self



***********************************
METHOD Destroy() CLASS RecordPicker

   ::lRecScope      := NIL
   ::cScopeTop      := NIL
   ::cScopeBottom   := NIL
   ::cSeekPrefix    := NIL

   ::cIndexFile     := NIL
   ::cOrderBag      := NIL

   ::aComboPick_    := NIL
   ::aComboBlock_   := NIL
   ::cWorkarea      := NIL
   ::aBrwFields_    := NIL
   ::aBrwColLabels_ := NIL 
   ::aColWidths_    := NIL
   ::cWndTitle      := NIL
   
   ::bCustomSearch  := NIL          // code block to call external UDF for seeking records
   
   ::cImageHome     := NIL
   
   ::aSelection_    := NIL

   RETURN Self


************************************
METHOD Activate() CLASS RecordPicker

   LOCAL RetVal := {}
   LOCAL cThisWorkArea := ::cWorkArea
   
   IF ISWINDOWDEFINED( frmPicker )
      DOMETHOD( "frmPicker","Restore" )
      DOMETHOD( "frmPicker","Setfocus")
      RETURN NIL
   ENDIF
   
   DEFINE WINDOW frmPicker;
      AT 0, 0;
      WIDTH 468 HEIGHT 347;
      TITLE ::cWndTitle;
      ICON ::cImageHome + "DaleAid.ico";
      TOPMOST NOMINIMIZE NOMAXIMIZE NOSIZE;
      FONT "Arial" SIZE 9;
      ON RELEASE NIL;
      ON INTERACTIVECLOSE NIL
         
      @  13, 10 LABEL lblSearch OF frmPicker VALUE "Search by"; 
         ACTION Nil WIDTH  60 HEIGHT 20 FONT "Arial" SIZE 9
         
      @  10, 70 COMBOBOX cboOrderBag OF frmPicker ITEMS ::aComboPick_ VALUE 1;
         WIDTH  100 HEIGHT 100 FONT "Arial" SIZE 9;
         TOOLTIP NIL;
         ON CHANGE ::ComboboxChange() ON LOSTFOCUS Nil
         
      @  13,180 LABEL lblFind OF frmPicker VALUE "&Find:"; 
         ACTION Nil WIDTH  30 HEIGHT 20 FONT "Arial" SIZE 9
         
      @  40, 10 FRAME Frame_1 OF frmPicker CAPTION "Contents at &Glance:"; 
         WIDTH  440 HEIGHT 230 FONT "Arial" SIZE 9 BACKCOLOR NIL FONTCOLOR NIL OPAQUE
         
      @  57, 15 BROWSE brwBrowsePicker OF frmPicker WIDTH  430 HEIGHT 206; 
         HEADERS ::aBrwColLabels_ WIDTHS ::aColWidths_ WORKAREA &cThisWorkArea FIELDS ::aBrwFields_; 
         FONT "Arial" SIZE 9 ON DBLCLICK ::BtnOKSelection( @RetVal ); 
         ON HEADCLICK Nil
         
      @  10,210 TEXTBOX txbQFind OF frmPicker HEIGHT 24 VALUE "" WIDTH  240;
         FONT "Arial" SIZE 9 TOOLTIP "" MAXLENGTH 255 ON CHANGE ::QSearch() ON LOSTFOCUS Nil;
         ON ENTER frmPicker.brwBrowsePicker.Setfocus
         
      @ 277,350 BUTTON btnCancel OF frmPicker CAPTION "&Cancel"; 
         PICTURE ::cImageHome + "cancel.bmp" LEFT ACTION frmPicker.Release ;
         WIDTH  100 HEIGHT 28
         
      @ 277,250 BUTTON btnOk OF frmPicker CAPTION "&Ok"; 
         PICTURE ::cImageHome + "ok.bmp" LEFT ACTION ::BtnOKSelection( @RetVal );
         WIDTH  100 HEIGHT 28
         
         
   END WINDOW
   
   ::ComboboxChange()
   
   frmPicker.Center
   frmPicker.Activate

   RETURN RetVal


******************************************
METHOD ComboboxChange() CLASS RecordPicker

   LOCAL cboTooltip := ::aComboPick_[ frmPicker.cboOrderBag.Value ]
   LOCAL bAction    := ::aComboBlock_[ frmPicker.cboOrderBag.Value ]
   
   frmPicker.cboOrderBag.Tooltip := cboTooltip
   frmPicker.txbQFind.Setfocus
   frmPicker.Title := ::cWndTitle + " - (" + cboTooltip + ")"
   EVAL( bAction )
   frmPicker.brwBrowsePicker.Value := RECNO()
   frmPicker.brwBrowsePicker.Refresh
   RETURN NIL
   


****************************************
METHOD QSearch() CLASS RecordPicker

   LOCAL cFindThis
   LOCAL nThisRec  := RECNO()
   
   SET SOFTSEEK ON
   IF ::lRecScope
      cFindThis := ::cSeekPrefix + ALLTRIM( frmPicker.txbQFind.Value )
   ELSE
      cFindThis := ALLTRIM( frmPicker.txbQFind.Value )
   ENDIF
                     
   DBSEEK( cFindThis )
   IF EOF()
      DBGOTO( nThisRec )
   ENDIF
   frmPicker.brwBrowsePicker.Value := RECNO()
   frmPicker.brwBrowsePicker.Refresh
   SET SOFTSEEK OFF
   RETURN NIL



********************************************************
METHOD BtnOKSelection( aPickList_ ) CLASS RecordPicker

   LOCAL nStart := 0
   LOCAL nEnd   := LEN( ::aSelection_ )
   
   FOR nStart = 1 TO nEnd
      AADD( aPickList_, FIELDGET( ::aSelection_[ nStart ] ) )
   NEXT
   frmPicker.Release
   RETURN NIL




*********************************************
METHOD PickStatus( aVar_ ) CLASS RecordPicker

   RETURN ( LEN( aVar_ ) > 0 )





Somewhere in another .prg file, I call Record Picker as follows:

Code: Select all

         oPick := RecordPicker():New( aPickList_, abPickAction_, "CHARTACCT", aFields_, aLabels_, aWidths_ )
         oPick:cImageHome := ImageHome
         oPick:lRecScope := FALSE
         oPick:cSeekPrefix := ACCTCLASS->F1 + ACCTCLASS->F2
         oPick:aSelection_ := { 3, 4 }
      
         TVar1 := oPick:Activate()
         IF oPick:PickStatus( TVar1 ) = TRUE
            frmGUI_3.txbParentCode.Value  := TVar1 [ 1 ]
            frmGUI_3.txbParentTitle.Value := TVar1 [ 2 ]
            frmGUI_3.btnSave.SetFocus
         ELSE
            frmGUI_3.btnSave.SetFocus
         ENDIF   
         oPick:Destroy()
Before the call to it, the table must be prepared, the index must be set properly. The actual search is performed by METHOD QSearch(), an incremental search facility that reposition the record pointer to the next higher index key. By the way I use tables so if you use MySQL then you have to adjust accordingly.


RecordPicker works like this:

oPick := RecordPicker():New( aPickList_, abPickAction_, "CHARTACCT", aFields_, aLabels_, aWidths_ )
--> the code above create an instance of the class and select the proper workarea
oPick:cImageHome := ImageHome ------> this set's the folder where images or graphics are stored
oPick:lRecScope := FALSE ---> this must be logical .t. if ORDSCOPE() is use and enforce on the workarea
specified by the alias in the NEW class constructor call above
oPick:cSeekPrefix := ACCTCLASS->F1 + ACCTCLASS->F2 --> if more than one field is combined in the index
oPick:aSelection_ := { 3, 4 } --> this is the field number that I wish to return

TVar1 := oPick:Activate() --> this would display the modal window
IF oPick:PickStatus( TVar1 ) = TRUE ----> this would tell if ok button is press
because the return value is set when OK button is press.
Other wise it returns an empty array.
frmGUI_3.txbParentCode.Value := TVar1 [ 1 ]
frmGUI_3.txbParentTitle.Value := TVar1 [ 2 ]
frmGUI_3.btnSave.SetFocus
ELSE
frmGUI_3.btnSave.SetFocus
ENDIF
oPick:Destroy()

Regards,

Danny
Regards,

Danny
Manila, Philippines
User avatar
dhaine_adp
Posts: 457
Joined: Wed Aug 06, 2008 12:22 pm
Location: Manila, Philippines

Re: Return value from modal window

Post by dhaine_adp »

Hi Sudip,

In addition, I don't give fieldname specific name and instead used F1, F2... and so on to safe guard the database to users who might used excel to view or import the dbf and unknowingly corrupted it. Sometimes there users who are curious that constantly give headache. :D So if they don't understand the contents and the relation of each dbf they would stop and just marvel. Besides 50% of a system is told by the database itself to the eyes of the programmer. This is how I put the tables and I believed is equally easy to convert to MySQL script or Oracle script.

Code: Select all

   IF .NOT. FILE( "&ChartAcct" + ".DBF" )
      DBCREATE( ( ChartAcct ),;
                              { { "F1"  , "C",  1, 0 } , ;   // F/S Account Type Code
                                { "F2"  , "C",  3, 0 } , ;   // Account Classification Code 
                                { "F3"  , "C", 13, 0 } , ;   // Account Number or Code
                                { "F4"  , "C",100, 0 } , ;   // Description
                                { "F5"  , "C", 13, 0 } , ;   // Parent Account Number or Code
                                { "F6"  , "N",  1, 0 } , ;   // Level or Indention
                                { "F7"  , "L",  1, 0 } , ;   // Account Status (.T. = Active, .F. = Inactive)
                                { "F8"  , "L",  1, 0 } , ;   // Show as Attachment or Itemize (.T. = Attachment, .F. = Itemize)
                                { "F9"  , "D",  8, 0 } , ;   // Date this Account Made Inactive
                                { "F10" , "D",  8, 0 } , ;   // Date this Account can be deleted
                                { "F11" , "N", 13, 2 } , ;   // TOTAL LY Month 1  (Last Year)
                                { "F12" , "N", 13, 2 } , ;   // TOTAL LY Month 2
                                { "F13" , "N", 13, 2 } , ;   // TOTAL LY Month 3
                                { "F14" , "N", 13, 2 } , ;   // TOTAL LY Month 4
                                { "F15" , "N", 13, 2 } , ;   // TOTAL LY Month 5
                                { "F16" , "N", 13, 2 } , ;   // TOTAL LY Month 6
                                { "F17" , "N", 13, 2 } , ;   // TOTAL LY Month 7
                                { "F18" , "N", 13, 2 } , ;   // TOTAL LY Month 8
                                { "F19" , "N", 13, 2 } , ;   // TOTAL LY Month 9
                                { "F20" , "N", 13, 2 } , ;   // TOTAL LY Month 10
                                { "F21" , "N", 13, 2 } , ;   // TOTAL LY Month 11
                                { "F22" , "N", 13, 2 } , ;   // TOTAL LY Month 12
                                { "F23" , "N", 13, 2 } , ;   // TOTAL TY Month 1  (This Year)
                                { "F24" , "N", 13, 2 } , ;   // TOTAL TY Month 2
                                { "F25" , "N", 13, 2 } , ;   // TOTAL TY Month 3
                                { "F26" , "N", 13, 2 } , ;   // TOTAL TY Month 4
                                { "F27" , "N", 13, 2 } , ;   // TOTAL TY Month 5
                                { "F28" , "N", 13, 2 } , ;   // TOTAL TY Month 6
                                { "F29" , "N", 13, 2 } , ;   // TOTAL TY Month 7
                                { "F30" , "N", 13, 2 } , ;   // TOTAL TY Month 8
                                { "F31" , "N", 13, 2 } , ;   // TOTAL TY Month 9
                                { "F32" , "N", 13, 2 } , ;   // TOTAL TY Month 10
                                { "F33" , "N", 13, 2 } , ;   // TOTAL TY Month 11
                                { "F34" , "N", 13, 2 }}, ;   // TOTAL TY Month 12
         RDDSETDEFAULT() )
   ENDIF
   IF .NOT. FILE( "&ChartAcct" + ".CDX" )
      IF .NOT. NETUSE( ( ChartAcct ), "CHARTACCT", Exclusive, ReadWrite )
         cBreakMsg := "Cannot open file " + ChartAcct + " exclusively."
         BREAK
      ENDIF
      INDEX ON F3       TAG AcctCode TO ( ChartAcct ) EVAL IndexStatBar( 1, "ACCTCODE" ) EVERY 1
      INDEX ON F5       TAG Parent   TO ( ChartAcct ) EVAL IndexStatBar( 2, "PARENT" )   EVERY 1
      INDEX ON F1+F2+F3 TAG FSClass  TO ( ChartAcct ) EVAL IndexStatBar( 3, "FSCLASS" )  EVERY 1
      INDEX ON F1+F2+F4 TAG ByTitle  TO ( ChartAcct ) EVAL IndexStatBar( 3, "BYTITLE" )  EVERY 1
      INDEX ON F4       TAG FSName   TO ( ChartAcct ) EVAL IndexStatBar( 4, "FSNAME" )   EVERY 1
      
      ORDLISTADD( ( ChartAcct ) )
      
      IF LASTREC() = 0
         ScriptCoopTitle()
      ENDIF
      CHARTACCT->( DBCLOSEAREA() )
   ENDIF

Danny
Regards,

Danny
Manila, Philippines
User avatar
sudip
Posts: 1454
Joined: Sat Mar 07, 2009 11:52 am
Location: Kolkata, WB, India

Re: Return value from modal window

Post by sudip »

Hello Danny,
Thank you so much. Excellent code, excellent idea!!! :)
May I use your code in my projects?
With best regards.
Sudip
With best regards,
Sudip
User avatar
dhaine_adp
Posts: 457
Joined: Wed Aug 06, 2008 12:22 pm
Location: Manila, Philippines

Re: Return value from modal window

Post by dhaine_adp »

Hi Sudip,

As I have said, I shared it, so please feel free.

Regards,

Danny
Regards,

Danny
Manila, Philippines
Post Reply