Module to make the work easier with XML Dialogs

Just starting out? Need help? Post your questions and find answers here.
User avatar
puretom
New User
New User
Posts: 2
Joined: Tue Mar 28, 2017 7:42 pm

Module to make the work easier with XML Dialogs

Post by puretom »

I am working nearly with XML Dialogs only, because it make the work with windows much easier.
What i don't like and what confuses me is, that there are several numbers needed when coding with XML Dialogs (e.g. Dialog Number, Window Number, XML Number).
Thats chaos for me and so i wrote a Module which makes the handling easier.

The Module is additionally also an Object/a Class, that is, you can have more instances of the same dialog, all of these mentioned numbers are now in one hand, encapsulated in that module, more, in that that instance.

A bit tricky appeared to be the connection to the public instance variables. I needed a second pointer with the same name of the object but with a '*' in front of it. Honestly, I can live with that solution.

Well, I didn't write the Class by hand, I wrote a small (and very naive implemented) Object Precompiler, it wrote the Class for me in a PB readable and compileable form.

I think I will make it possible to implement Modules without that OOP component also with my precompiler, simply because the declares and so on are a bit awkward, well, let's come back to topic.

At first the source class prior using the precompiler:

Code: Select all


Module Dialog
  
  Structure object
    window_no.i As Public
    dialog_no.i As Public
    xml_no.i As Public
  EndStructure
  
  Public Class xml_number ; Xml Number after NewCatch
  
  ; Helpers
  Class Procedure CheckXMLTree(xml_no)
    
    If xml_no=0 Or XMLStatus(xml_no)<>#PB_XML_Success
      MessageRequester("XML error",
                       "XML error: " + XMLError(xml_no) + " (Line: " + XMLErrorLine(xml_no) + ")")
      End
    EndIf
    
  EndProcedure
  Class Procedure OpenXMLDialog_(xml_no, window_name.s, x, y)
    
    Protected dialog_no = CreateDialog(#PB_Any)
    
    If OpenXMLDialog(dialog_no, xml_no, window_name, x, y)=0
      MessageRequester("Dialog error",
                       "Dialog error: " + DialogError(dialog_no))
      End
    EndIf
    
    ProcedureReturn dialog_no
    
  EndProcedure
  
  ; Constructors, Open the window
  Public Class Procedure NewCatchXML(window_name.s, adress, size, free_xml=#False, x=0, y=0)
    
    ; new instance
    Protected *this.object = _New_()
    
    ; create xml tree & check it
    *this\xml_no = CatchXML(#PB_Any, adress, size)
    xml_number   = *this\xml_no
    CheckXMLTree(*this\xml_no)
    
    ; create dialog window
    *this\dialog_no = OpenXMLDialog_(*this\xml_no, window_name, x, y)
    If free_xml
      FreeXML(*this\xml_no)
      *this\xml_no = 0
      xml_number = 0
    EndIf
    
    ; get window number (e.g. EventWindow() = window_no)
    *this\window_no = DialogWindow(*this\dialog_no)
    
    ; returns instance pointer
    ProcedureReturn *this
    
  EndProcedure
  Public Class Procedure NewParseXML(window_name.s, string_name.s, free_xml=#False, x=0, y=0)
    
    ; new instance
    Protected *this.object = _New_() 
    
    ; create xml tree & check it
    *this\xml_no = ParseXML(#PB_Any, string_name)
    xml_number   = *this\xml_no
    CheckXMLTree(*this\xml_no)
    
    ; create dialog dialog
    *this\dialog_no = OpenXMLDialog_(*this\xml_no, window_name, x, y)
    If free_xml
      FreeXML(*this\xml_no)
      *this\xml_no = 0
      xml_number = 0
    EndIf
    
    ; get window number (for e.g. EventWindow() = window_no)
    *this\window_no = DialogWindow(*this\dialog_no) 
    
    ; returns instance pointer
    ProcedureReturn *this                           
    
  EndProcedure
  Public Class Procedure New(window_name.s, xml_no, x=0, y=0, free_xml=#False)
    
    ; new instance
    Protected *this.object = _New_() 
    
    ; check xml tree
    *this\xml_no = xml_no 
    CheckXMLTree(xml_no)
    
    ; create dialog dialog
    *this\dialog_no = OpenXMLDialog_(*this\xml_no, window_name, x, y)
    
    ; get window number (for e.g. EventWindow() = window_no)
    *this\window_no = DialogWindow(*this\dialog_no) 
    
    ; returns instance pointer
    ProcedureReturn *this                           
    
  EndProcedure
  
  ; Destructor  
  Public Procedure Free(*this.object)
    FreeDialog(*this\dialog_no)
    _Free_(*this)
  EndProcedure
  
EndModule

; Create a dialog Object with:
;     Global Window.Dialog
;     Window = Dialog::NewCatch(..)

; Create pointer to private instance variables (if needed)
; dont use _method_table_:
;     Global *Window.Dialog::Variables
;     *Window = Window  <--- important

; Free:
;     Window = Dialog::Free()
;     -> Window will be 0, and can be tested

This codes my precompiler based on that source class:

Code: Select all


; Instance Interface of Object with Public Instance Procedures (Methods)
Interface Dialog
  Free.i()
EndInterface
DeclareModule Dialog
  EnableExplicit

  ; Public Enumeration

  ; Public Structure

  ; Structure of Public Instance Variables (Attributes)
  Structure Variables
    *_method_table_
    window_no.i
    dialog_no.i
    xml_no.i
  EndStructure

  ; Declare Public Class Variables (Attributes)
  Global xml_number.i

  ; Declare Public Class Procedures (Methods)
  Declare.i New(window_name.s, xml_no, x=0, y=0, free_xml=#False)
  Declare.i NewCatchXML(window_name.s, adress, size, free_xml=#False, x=0, y=0)
  Declare.i NewParseXML(window_name.s, string_name.s, free_xml=#False, x=0, y=0)

EndDeclareModule
Module Dialog
  EnableExplicit

  ; Private Enumeration

  ; Private Structure

  ; Structure of Private Instance Variables (Attributes)
  Structure object Extends Variables
  EndStructure

  ; Declare Private Class Variables (Attributes)

  ; Declare all Instance & Private Class Procedures (Methods)
  Declare.i OpenXMLDialog_(xml_no, window_name.s, x, y)
  Declare.i CheckXMLTree(xml_no)
  Declare.i _Free_(*this.object)
  Declare.i _New_()
  Declare.i Free(*this.object)

  ; Procedures (Methods) Implementation
  Procedure.i OpenXMLDialog_(xml_no, window_name.s, x, y)
    Protected dialog_no = CreateDialog(#PB_Any)
    If OpenXMLDialog(dialog_no, xml_no, window_name, x, y)=0
    MessageRequester("Dialog error",
    "Dialog error: " + DialogError(dialog_no))
    End
    EndIf
    ProcedureReturn dialog_no
  EndProcedure
  Procedure.i CheckXMLTree(xml_no)
    If xml_no=0 Or XMLStatus(xml_no)<>#PB_XML_Success
    MessageRequester("XML error",
    "XML error: " + XMLError(xml_no) + " (Line: " + XMLErrorLine(xml_no) + ")")
    End
    EndIf
  EndProcedure
  Procedure.i New(window_name.s, xml_no, x=0, y=0, free_xml=#False)
    Protected *this.object = _New_()
    *this\xml_no = xml_no
    CheckXMLTree(xml_no)
    *this\dialog_no = OpenXMLDialog_(*this\xml_no, window_name, x, y)
    *this\window_no = DialogWindow(*this\dialog_no)
    ProcedureReturn *this
  EndProcedure
  Procedure.i _Free_(*this.object)
    FreeStructure(*this)
  EndProcedure
  Procedure.i Free(*this.object)
    FreeDialog(*this\dialog_no)
    _Free_(*this)
  EndProcedure
  Procedure.i _New_()
    Protected *new.object = AllocateStructure(object)
    If *new: *new\_method_table_ = ?method_table
    Else: MessageRequester("Runtime Class Allocation Error","Allocation of Object 'Dialog' failed."): End
    EndIf
    ProcedureReturn *new
  EndProcedure
  Procedure.i NewCatchXML(window_name.s, adress, size, free_xml=#False, x=0, y=0)
    Protected *this.object = _New_()
    *this\xml_no = CatchXML(#PB_Any, adress, size)
    xml_number   = *this\xml_no
    CheckXMLTree(*this\xml_no)
    *this\dialog_no = OpenXMLDialog_(*this\xml_no, window_name, x, y)
    If free_xml
    FreeXML(*this\xml_no)
    *this\xml_no = 0
    EndIf
    *this\window_no = DialogWindow(*this\dialog_no)
    ProcedureReturn *this
  EndProcedure
  Procedure.i NewParseXML(window_name.s, string_name.s, free_xml=#False, x=0, y=0)
    Protected *this.object = _New_()
    *this\xml_no = ParseXML(#PB_Any, string_name)
    xml_number   = *this\xml_no
    CheckXMLTree(*this\xml_no)
    *this\dialog_no = OpenXMLDialog_(*this\xml_no, window_name, x, y)
    If free_xml
    FreeXML(*this\xml_no)
    *this\xml_no = 0
    EndIf
    *this\window_no = DialogWindow(*this\dialog_no)
    ProcedureReturn *this
  EndProcedure

  ; DataSection of Public Instance Procedures (Methods)
  DataSection
    method_table:
    Data.i @Free()
  EndDataSection

EndModule

Please test the code, if someone finds that stuff useable, I just started to learn simple basic OOP and about objects. So this here is far away from being 100 percent tested.

Greetings Tom and thank you :-)

Testcode in next posting

This is also posted in german forum: http://forums.purebasic.com/german/view ... d6#p340305
Windows 7 and Windows 10 (Laptop), PB 5.60 | Projekt: Tutorial - Compiler and Virtual Machine (in German) | maybe sometimes an Old School Text-Adventure Tutorial | New: Tinkering around, Learning OOP in PB
User avatar
puretom
New User
New User
Posts: 2
Joined: Tue Mar 28, 2017 7:42 pm

Re: Module to make the work easier with XML Dialogs

Post by puretom »

The testcode.

You can see, how an instance is created and how it is destroyed.

The EventLoop() shows, that you have to do an If-Question to asure that the instance pointer is not zero.
If you don't do that you will run into troubles after a window/dialog is closed and the event loop wants to check for the window number for example.

Also the tricked connection to the public instance variables (as said over a pointer) is implemented.
On could go around that with getters easily, but I decided to go with the pointer because of performance and avoiding overhead. You can change that quickly in no time if you want.

Beside that it seems to work, but as i said above, please test it if you find it useful, it's far away from being 100 percent I fear.

I am thankfully for help and bug reports.

Greets Tom <)

The testcode:

Code: Select all

XIncludeFile "Class Dialog.pb"
; DataSection ; Dialog XML
;   begin_dialog: :IncludeBinary "_dialogs.xml":end_dialog:
; EndDataSection

Global xml$
xml$+"<Dialogs>"
xml$+"  <Window name='wndMain' text='Dialog from the string' width='800' height='650' flags='#PB_Window_SystemMenu|#PB_Window_MinimizeGadget|#PB_Window_MaximizeGadget|#PB_Window_SizeGadget|#PB_Window_ScreenCentered'>"
xml$+"    <vbox>"
xml$+"      <button text='Load' onevent='buttonLoad()' />"
xml$+"      <button text='Save' />"
xml$+"      <button text='Credits' />"
xml$+"    </vbox>"
xml$+"  </Window>"
xml$+"</Dialogs>"
  
Global MainWindow.Dialog
Global *MainWindow.Dialog::Variables

Global Window2.Dialog
Global *Window2.Dialog::Variables

Global Window3.Dialog
Global *Window3.Dialog::Variables

Procedure EventLoop()
  
  Protected event
  
  Repeat
    event = WaitWindowEvent()
    
    If MainWindow And EventWindow() = *MainWindow\window_no 
      If event = #PB_Event_CloseWindow
        MainWindow = MainWindow\Free()
        End
        ;End ; or not End if it was not the Main Window
      EndIf
      
    ElseIf Window2 And EventWindow() = *Window2\window_no
      If event = #PB_Event_CloseWindow
        Window2 = Window2\Free()
      EndIf
      
    ElseIf Window3 And EventWindow() = *Window3\window_no
      If event = #PB_Event_CloseWindow
        Window3 = Window3\Free()
      EndIf
    EndIf
    
  ForEver
  
EndProcedure
Procedure Main()
  
  ;MainWindow = Dialog::NewCatchXML("wndMain", ?begin_dialog, ?end_dialog-?begin_dialog) ; <--- use with datasection
  MainWindow = Dialog::NewParseXML("wndMain", xml$) ; <--- use with string
  *MainWindow = MainWindow
  SetWindowTitle(*MainWindow\window_no, "Main Window - closing me means end")
  
  Debug "*MainWindow\window_no = "+*MainWindow\window_no
  Debug "*MainWindow\dialog_no = "+*MainWindow\dialog_no
  Debug "*MainWindow\xml_no    = "+*MainWindow\xml_no
  Debug ""
  Debug "Dialog::xml_number    = "+Dialog::xml_number
  
  
  Debug ""
  Window2  = Dialog::New("wndMain", Dialog::xml_number) ; <--- use to open with existing xml tree in memory
  *Window2 = Window2
  SetWindowTitle(*Window2\window_no, "Window 2")
  
  Debug "*Window2\window_no    = "+*Window2\window_no
  Debug "*Window2\dialog_no    = "+*Window2\dialog_no
  Debug "*Window2\xml_no       = "+*Window2\xml_no
  Debug ""
  Debug "Dialog::xml_number    = "+Dialog::xml_number
  
  Window3  = Dialog::New("wndMain", Dialog::xml_number)
  *Window3 = Window3
  SetWindowTitle(*Window3\window_no, "Window 3")
  
  Debug "*Window3\window_no    = "+*Window3\window_no
  Debug "*Window3\dialog_no    = "+*Window3\dialog_no
  Debug "*Window3\xml_no       = "+*Window3\xml_no
  Debug ""
  Debug "Dialog::xml_number    = "+Dialog::xml_number
  Debug "------------------------------------------------------"
  
EventLoop()
  
EndProcedure

Runtime Procedure buttonLoad()
  
  Protected window_no = EventWindow()
  
  Select window_no
    Case *MainWindow\window_no
      Debug "buttonLoad -> *MainWindow: "+window_no
    Case *Window2\window_no
      Debug "buttonLoad -> *Window2: "+window_no      
    Case *Window3\window_no
      Debug "buttonLoad -> *Window3: "+window_no      
  EndSelect
  
EndProcedure

Main()

Windows 7 and Windows 10 (Laptop), PB 5.60 | Projekt: Tutorial - Compiler and Virtual Machine (in German) | maybe sometimes an Old School Text-Adventure Tutorial | New: Tinkering around, Learning OOP in PB
Post Reply