Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware API
      • ZBrush GoZ API
      • Code Examples on Github
    • Forum
    • Downloads
    • Support
      • Support Procedures
      • Registered Developer Program
      • Plugin IDs
      • Contact Us
    • Categories
      • Overview
      • News & Information
      • Cinema 4D SDK Support
      • Cineware SDK Support
      • ZBrush 4D SDK Support
      • Bugs
      • General Talk
    • Unread
    • Recent
    • Tags
    • Users
    • Login

    Messages between objects [SOLVED]

    PYTHON Development
    0
    24
    14.0k
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • H
      Helper
      last edited by

      On 03/04/2015 at 19:07, xxxxxxxx wrote:

      Hello,

      thinking about communication between objects in a scene, it would be great if someone can give me a hint.
      E.g. I have a python generator object in my scene with a user data link field.
      The geometry of the linked object is part of the generator calculation.
      With disabled cache optimization I can handle the dirty state of the generator by myself.
      Additionally with another global dirty variable for the linked object I can detect if the linked one has changed that the generator can update properly.

      Is there a better way to manage such a task, it seems dirty?

      Thanks in advance
      Martin

      1 Reply Last reply Reply Quote 0
      • H
        Helper
        last edited by

        On 07/04/2015 at 07:43, xxxxxxxx wrote:

        Hi Martin,

        You seem to be doing this the right way.
        But how do you handle the global dirty variable for the linked object?
        Also what method do you call? C4DAtom.GetDirty() or BaseObject.IsDirty() or C4DAtom.GetHDirty()?

        1 Reply Last reply Reply Quote 0
        • H
          Helper
          last edited by

          On 09/04/2015 at 00:02, xxxxxxxx wrote:

          Hi Martin,

          What I do is instead of having a link, I put the object as a child under my generator.
          This way the generator is triggered if the object is changed or an object is placed under the generator.

          -Pim

          1 Reply Last reply Reply Quote 0
          • H
            Helper
            last edited by

            On 09/04/2015 at 02:46, xxxxxxxx wrote:

            Hello,

            @Pim:
            This doesn´t work for me. I need unique link fields for several objects which are treated in a specific order and way. Therefore I don´t want to force the user to keep an eye on a hierarchy or even don´t want to explain it to the user. Nevertheless, could you post an example please, I can´t see how your suggestion should work with e.g. point modifcations or deformer.

            @Yannick
            I´ll post a simple example here.
            For the linked object I use GetDirty() and raise the global variable of my generator to update accordingly.
            I would be glad if there is a better way and also if the example is the right way. Thanks!

            Best wishes
            Martin

              
            import c4d, random  
            from c4d import utils  
              
            global dcount  
            dcount = 0  
               
              
            def GetLink(op) :  
              link = op[c4d.ID_USERDATA,1]  
              if not link:return  
              if not link.IsInstanceOf(c4d.Opolygon) :link = link.GetCache()  
              deform = link.GetDeformCache()  
              if deform:  
                  link = deform  
              return link  
              
            def main() :  
              global dcount  
              op[c4d.OPYTHON_OPTIMIZE] = False  
              vor = GetLink(op)  
              if not vor:return  
                   
              #_____________________________________________________  
              #custom dirty test with linked object             
              vdirty = vor.GetDirty(c4d.DIRTY_DATA|c4d.DIRTY_MATRIX)  
              if vor.GetDirty(c4d.DIRTY_DATA|c4d.DIRTY_MATRIX) :          
                  if dcount != vdirty:  
                      dcount = vdirty  
                      op.SetDirty(c4d.DIRTY_DATA|c4d.DIRTY_MATRIX)            
              if not op.IsDirty(c4d.DIRTY_DATA|c4d.DIRTY_MATRIX) :return op.GetCache()  
                
                
              #_____________________________________________________  
              #parent virtual object  
              null = c4d.BaseObject(c4d.Onull)  
              child = vor.GetClone()  
              #child.SetMg(op.GetMg())  
              child.InsertUnder(null)  
                
              return null  
              
            
            1 Reply Last reply Reply Quote 0
            • H
              Helper
              last edited by

              On 09/04/2015 at 03:32, xxxxxxxx wrote:

              Hi Martin,

              I have a text and spline below the generator:
              - first I check whether both are give, using node.GetDown()
              - then I check whether another object is given, using a previous value
              - then I check whether the objects are changed (dirty)
              In these case I set the node to dirty

              If the node is dirty - do whatever you want to do.

              You know me, perhaps not optimal and efficient, but it is working.

                  def GetVirtualObjects(self, node, hierarchyhelp) :
                  
                      doc = documents.GetActiveDocument()
                      frame = doc.GetTime().GetFrame(doc.GetFps()) 
                
                      #check of text+spline is given
                      if (node.GetDown() is None) : return             #Text
                      if (node.GetDown().GetNext() is None) : return   #Spline
                       
                      # Text given changed?
                      if node.GetDown() != self.text:
                          self.text = node.GetDown()
                          #print "text source: ", self.text
                          node.SetDirty(c4d.DIRTY_DATA)
                
                      # Spline given changed?            
                      if (self.text.GetNext() != self.boven) :
                          self.boven = self.text.GetNext()
                          node.SetDirty(c4d.DIRTY_DATA)
                      
                      if (self.boven.GetType() != c4d.Ospline) : 
                          #print "Not a spline"
                          return
                      
                      # Text changed (dirty)?
                      if node.GetDown() and node.GetDown().IsDirty(c4d.DIRTY_DATA | c4d.DIRTY_MATRIX) :
                          node.SetDirty(c4d.DIRTY_DATA)
                          #print "Set dirty text."
                
                      # Spline changed (dirty)?
                      if node.GetDown().GetNext() and node.GetDown().GetNext().IsDirty(c4d.DIRTY_DATA | c4d.DIRTY_MATRIX) :
                          node.SetDirty(c4d.DIRTY_DATA)
                          #print "Set dirty spline boven."
                
              .....
              

              -Pim

              1 Reply Last reply Reply Quote 0
              • H
                Helper
                last edited by

                On 09/04/2015 at 03:59, xxxxxxxx wrote:

                Hi Martin,

                Originally posted by xxxxxxxx

                @Yannick
                I´ll post a simple example here.
                For the linked object I use GetDirty() and raise the global variable of my generator to update accordingly.
                I would be glad if there is a better way and also if the example is the right way. Thanks!

                Your Python generator script seems to work fine but I've found 2 minor issues:

                To prevent errors in GetLink() you should check if the user data exists and if it's really a link:

                def GetLink(op) :
                    udOK = False
                    for udID, udBC in op.GetUserDataContainer() :
                        if udID[1].id==1 and udID[1].dtype==c4d.DTYPE_BASELISTLINK:
                            udOK = True
                            break
                    
                    print "User Data OK : " + str(udOK)
                    if not udOK: return
                    
                    link = op[c4d.ID_USERDATA,1]
                    if not link:return
                    if not link.IsInstanceOf(c4d.Opolygon) :link = link.GetCache()
                    deform = link.GetDeformCache()
                    if deform:
                        link = deform
                    return link
                

                Also the document's dirty state is always set because "Optimize Cache" is disabled in the generator main():

                  
                op[c4d.OPYTHON_OPTIMIZE] = False
                

                This should be disabled upon generator's creation and a warning should be printed if it's enabled again.

                1 Reply Last reply Reply Quote 0
                • H
                  Helper
                  last edited by

                  On 09/04/2015 at 07:07, xxxxxxxx wrote:

                  Hi Pim, Hi Yannick

                  @Pim
                  The problem with the IsDirty() function and a generator object is, that IsDirty() seems to be hierarchy dependent.
                  Within your child structure this might be possible, but with a link field where the object can be anywhere in the hierarchy your example wont work. Anyway, thanks for your effort.

                  @Yannick
                  Thanks for the hints and lines!

                  Originally posted by xxxxxxxx

                  To prevent errors in GetLink() you should check if the user data exists and if it's really a link:

                  Thinking further about what could happen in the worst case, I figured out that there is even another case I need to cover.

                      if not link.IsInstanceOf(c4d.Opolygon) :link = link.GetCache()
                  

                  This line produces an error if the linked atom is a material. I used try except...

                  Another problem occured!
                  If the linked object is a primitiv, the dirty check only triggers once and after that the generator wont update anymore.
                  Could you please tell me what´s wrong with the code?

                    
                  import c4d, random  
                  from c4d import utils,gui  
                    
                  global dcount  
                  dcount = 0  
                     
                    
                  def GetLink(op) :  
                    udOK = False  
                    for udID, udBC in op.GetUserDataContainer() :  
                        if udID[1].id==1 and udID[1].dtype==c4d.DTYPE_BASELISTLINK:  
                            udOK = True  
                            break          
                    if not udOK:          
                        dialog=gui.MessageDialog("No appropriate UserData available! \n"  
                                                "Please insert a linkfield \n"  
                                                 "and activate the generator again.",c4d.GEMB_OK)  
                        op[c4d.ID_BASEOBJECT_GENERATOR_FLAG] = False    
                        return  
                    link = op[c4d.ID_USERDATA,1]  
                    if not link: return  
                    if not link.IsInstanceOf(c4d.Opolygon) :   
                        try:  
                            link = link.GetCache()  
                        except: return  
                          
                    deform = link.GetDeformCache()  
                    if deform:  
                        link = deform  
                    return link  
                    
                  def main() :  
                    global dcount  
                    if op[c4d.OPYTHON_OPTIMIZE] == True:  
                        dialog=gui.MessageDialog("Please uncheck Optimize Cache! \n"  
                                                 "and activate the generator again.",c4d.GEMB_OK)  
                        op[c4d.ID_BASEOBJECT_GENERATOR_FLAG] = False   
                        return          
                    vor = GetLink(op)  
                    if not vor:return  
                         
                    #_____________________________________________________  
                    #custom dirty test with linked object             
                    vdirty = vor.GetDirty(c4d.DIRTY_DATA|c4d.DIRTY_MATRIX)  
                    
                    if vdirty:                
                        if dcount != vdirty:  
                            print "dirty"  
                            dcount = vdirty  
                            op.SetDirty(c4d.DIRTY_DATA|c4d.DIRTY_MATRIX)            
                    if not op.IsDirty(c4d.DIRTY_DATA|c4d.DIRTY_MATRIX) :return op.GetCache()  
                      
                      
                    #_____________________________________________________  
                    #parent virtual object  
                    null = c4d.BaseObject(c4d.Onull)  
                    child = vor.GetClone()  
                    #child.SetMg(op.GetMg())  
                    child.InsertUnder(null)  
                      
                    return null  
                    
                  

                  Thanks in advance !
                  Best wishes
                  Martin

                  1 Reply Last reply Reply Quote 0
                  • H
                    Helper
                    last edited by

                    On 09/04/2015 at 09:24, xxxxxxxx wrote:

                    Originally posted by xxxxxxxx

                    if not link.IsInstanceOf(c4d.Opolygon) :link = link.GetCache()
                    This line produces an error if the linked atom is a material. I used try except...

                    I think you'd better check the classification of the linked base list and discard all that aren't objects, in Obase classification.

                    Originally posted by xxxxxxxx

                    Another problem occured!
                    If the linked object is a primitiv, the dirty check only triggers once and after that the generator wont update anymore.
                    Could you please tell me what´s wrong with the code?

                    You're getting the dirty checksum of the cache, not of the actual object, in the case of a primitive.
                    I think you'd better have 2 functions: one to get the linked object and another to get the linked geometry.

                    Also, an object (at least an empty Null object in the case of an error) should be always returned by a generator, otherwise Cinema 4D will try to rebuild the cache again and again.

                    Here's the working code I came up with:

                    import c4d
                    from c4d import utils, gui
                      
                    global dirtyCount
                    dirtyCount = 0
                      
                    def GetLink(op) :
                        udOK = False
                        for udID, udBC in op.GetUserDataContainer() :
                            if udID[1].id==1 and udID[1].dtype==c4d.DTYPE_BASELISTLINK:
                                udOK = True
                                break
                        if not udOK:
                            dialog = gui.MessageDialog("No appropriate UserData available! \n"
                                                       "Please insert a linkfield \n"
                                                       "and activate the generator again.",c4d.GEMB_OK)
                            op[c4d.ID_BASEOBJECT_GENERATOR_FLAG] = False
                            return None
                        
                        link = op[c4d.ID_USERDATA,1]
                      
                        if link.GetClassification()!=c4d.Obase:
                            dialog = gui.MessageDialog("Please insert a valid object in the user data link!",c4d.GEMB_OK)
                            op[c4d.ID_BASEOBJECT_GENERATOR_FLAG] = False
                            return None
                        
                        return link
                        
                      
                    def GetGeom(op) :
                        geom = op
                        if not op.IsInstanceOf(c4d.Opolygon) :
                            geom = op.GetCache()
                        deform = geom.GetDeformCache()
                        if deform:
                            geom = deform
                        return geom
                      
                      
                    def main() :
                        global dirtyCount
                        
                        if op[c4d.OPYTHON_OPTIMIZE] == True:
                            dialog=gui.MessageDialog("Please uncheck Optimize Cache! \n"
                                                     "and activate the generator again.",c4d.GEMB_OK)
                            op[c4d.ID_BASEOBJECT_GENERATOR_FLAG] = False 
                            return c4d.BaseObject(c4d.Onull)
                        
                        link = GetLink(op)
                        if not link: return c4d.BaseObject(c4d.Onull)
                        
                        #custom dirty test with linked object
                        dirty = link.GetDirty(c4d.DIRTYFLAGS_DATA|c4d.DIRTYFLAGS_MATRIX)
                        if dirty != dirtyCount:
                            print "Dirty"
                            dirtyCount = dirty
                            op.SetDirty(c4d.DIRTYFLAGS_DATA|c4d.DIRTYFLAGS_MATRIX)
                        else:
                            print "Not Dirty"
                        
                        if not op.IsDirty(c4d.DIRTYFLAGS_DATA|c4d.DIRTYFLAGS_MATRIX) : return op.GetCache()
                        
                        geom = GetGeom(link)
                      
                        #parent virtual object
                        null = c4d.BaseObject(c4d.Onull)
                        child = geom.GetClone()
                        #child.SetMg(op.GetMg())
                        child.InsertUnder(null)
                        
                        return null
                    
                    1 Reply Last reply Reply Quote 0
                    • H
                      Helper
                      last edited by

                      On 09/04/2015 at 18:04, xxxxxxxx wrote:

                      Hi Yannick,

                      thanks a lot for developing this further with me!
                      That was the solution to differentiate the link itself and the geometry, great!
                      And good to know that the generator needs a least one object to return.
                      Two minor issues, but really minimal:
                      After line 20 in your code there must be another if not clause, if there is no object in the link field.
                      Otherwise an error occured None doesn´t have GetClassification().
                      And the GetGeom function needs to be called before the dirty check lines, that the deform cache could be taken into account while checking the dirty state.

                      Again, that was very helpful thanks.
                      One can do a lot of nice stuff with this combination, generator and linked object.

                      Best wishes
                      Martin

                      1 Reply Last reply Reply Quote 0
                      • H
                        Helper
                        last edited by

                        On 10/04/2015 at 00:33, xxxxxxxx wrote:

                        Hi Martin,

                        You're welcome. Thanks for pointing the errors in my code.
                        I'll tag the topic as solved.

                        1 Reply Last reply Reply Quote 0
                        • H
                          Helper
                          last edited by

                          On 10/04/2015 at 00:34, xxxxxxxx wrote:

                          Hi Yannick,

                          I guess I was overhasty stating that your code is the solution.
                          If you change an attribute of a primitiv in the link field it again wont work.
                          Also if you grab the handels and scale the primitiv, the generator won´t reflect this.
                          What else could be wrong?
                          Sorry to bother you again.

                          Best wishes
                          Martin

                          1 Reply Last reply Reply Quote 0
                          • H
                            Helper
                            last edited by

                            On 10/04/2015 at 00:36, xxxxxxxx wrote:

                            Originally posted by xxxxxxxx

                            I guess I was overhasty stating that your code is the solution.
                            If you change an attribute of a primitiv in the link field it again wont work.
                            Also if you grab the handels and scale the primitiv, the generator won´t reflect this.
                            What else could be wrong?

                            Are you checking the dirty on the linked primitive object and not on its cache?

                            1 Reply Last reply Reply Quote 0
                            • H
                              Helper
                              last edited by

                              On 10/04/2015 at 01:04, xxxxxxxx wrote:

                              Hi Yannick,

                              this is driving me nuts, even if I differentiate the dirtytest within the GetGeom function and pass the value.
                              I´m always one step behind the update.
                              If you e.g. change a parameter of the primitiv numerically and press enter, nothing happens.
                              Thanks in advance for your patience!

                              Martin

                                
                              import c4d  
                              from c4d import utils, gui  
                                
                              global dirtyCount  
                              dirtyCount = 0  
                                
                              def GetLink(op) :  
                                udOK = False  
                                for udID, udBC in op.GetUserDataContainer() :  
                                    if udID[1].id==1 and udID[1].dtype==c4d.DTYPE_BASELISTLINK:  
                                        udOK = True  
                                        break  
                                if not udOK:  
                                    dialog = gui.MessageDialog("No appropriate UserData available! \n"  
                                                               "Please insert a linkfield \n"  
                                                               "and activate the generator again.",c4d.GEMB_OK)  
                                    op[c4d.ID_BASEOBJECT_GENERATOR_FLAG] = False  
                                    return None  
                                  
                                link = op[c4d.ID_USERDATA,1]  
                                if not link:return None  
                                
                                if link.GetClassification()!=c4d.Obase:  
                                    dialog = gui.MessageDialog("Please insert a valid object in the user data link!",c4d.GEMB_OK)  
                                    op[c4d.ID_BASEOBJECT_GENERATOR_FLAG] = False  
                                    return None  
                                  
                                return link  
                                  
                                
                              def GetGeom(op) :  
                                geom = op  
                                dirty = op.GetDirty(c4d.DIRTYFLAGS_DATA|c4d.DIRTYFLAGS_MATRIX)  
                                if not op.IsInstanceOf(c4d.Opolygon) :  
                                    dirty = op.GetDirty(c4d.DIRTYFLAGS_DATA|c4d.DIRTYFLAGS_MATRIX)  
                                    geom = op.GetCache()  
                                deform = geom.GetDeformCache()  
                                if deform:         
                                    dirty = deform.GetDirty(c4d.DIRTYFLAGS_DATA|c4d.DIRTYFLAGS_MATRIX)  
                                    geom = deform  
                                return geom,dirty  
                                
                                
                              def main() :  
                                global dirtyCount  
                                  
                                if op[c4d.OPYTHON_OPTIMIZE] == True:  
                                    dialog=gui.MessageDialog("Please uncheck Optimize Cache! \n"  
                                                             "and activate the generator again.",c4d.GEMB_OK)  
                                    op[c4d.ID_BASEOBJECT_GENERATOR_FLAG] = False   
                                    return c4d.BaseObject(c4d.Onull)  
                                  
                                link = GetLink(op)  
                                if not link: return c4d.BaseObject(c4d.Onull)  
                                  
                                geom,dirty = GetGeom(link)  
                                  
                                  
                                  
                                #custom dirty test with linked object  
                                #dirty = link.GetDirty(c4d.DIRTYFLAGS_DATA|c4d.DIRTYFLAGS_MATRIX)  
                                if dirty != dirtyCount:          
                                    dirtyCount = dirty  
                                    op.SetDirty(c4d.DIRTYFLAGS_DATA|c4d.DIRTYFLAGS_MATRIX)  
                                 
                                  
                                if not op.IsDirty(c4d.DIRTYFLAGS_DATA|c4d.DIRTYFLAGS_MATRIX) : return op.GetCache()  
                                
                                #parent virtual object  
                                null = c4d.BaseObject(c4d.Onull)  
                                child = geom.GetClone()  
                                child.InsertUnder(null)  
                                  
                                return null  
                              
                              1 Reply Last reply Reply Quote 0
                              • H
                                Helper
                                last edited by

                                On 10/04/2015 at 01:14, xxxxxxxx wrote:

                                an additional note.
                                This behaviour is again hierarchy dependend.
                                If the primitiv is above the generator in the hierarchy everything works fine.
                                If the primitiv is below it won´t

                                1 Reply Last reply Reply Quote 0
                                • H
                                  Helper
                                  last edited by

                                  On 10/04/2015 at 02:57, xxxxxxxx wrote:

                                  It works fine for me with polygon objects, primitives, deformers etc.

                                  2 notes on GetGeom() :
                                  op.GetDirty() is called twice in the case of a primitive.
                                  It would be better to add the dirty count from op.GetDirty() and deform.GetDirty().

                                    
                                  def GetGeom(op) :  
                                    geom = op  
                                    dirty = op.GetDirty(c4d.DIRTYFLAGS_DATA|c4d.DIRTYFLAGS_MATRIX)  
                                    if not op.IsInstanceOf(c4d.Opolygon) :  
                                        ~~dirty = op.GetDirty(c4d.DIRTYFLAGS_DATA|c4d.DIRTYFLAGS_MATRIX)~~  
                                        geom = op.GetCache()
                                      deform = geom.GetDeformCache()  
                                      if deform:         
                                          dirty = **dirty +** deform.GetDirty(c4d.DIRTYFLAGS_DATA|c4d.DIRTYFLAGS_MATRIX)  
                                          geom = deform  
                                      return geom,dirty  
                                  
                                  1 Reply Last reply Reply Quote 0
                                  • H
                                    Helper
                                    last edited by

                                    On 10/04/2015 at 04:13, xxxxxxxx wrote:

                                    Hi Yannick,

                                    thanks for the next optimization hint!
                                    But still, the problem with the primitiv exists.
                                    I decided to make a small video and an example file to show you what I was talking about.
                                    I´ll send you a PM.

                                    Thanks for your staying power!
                                    Best wishes
                                    Martin

                                    1 Reply Last reply Reply Quote 0
                                    • H
                                      Helper
                                      last edited by

                                      On 10/04/2015 at 06:51, xxxxxxxx wrote:

                                      Hi Martin,

                                      Thanks for the example scene and video. I've been able to see the issue and reproduce it.
                                      It's strange this only happens when editing the primitive attribute by hand and pressing enter.

                                      I think the problem is with GetCache() in GetGeom(). The cache of the linked primitive object hasn't been updated yet when retrieving it.
                                      The only way to obtain the updated polygonal representation of a primitive object is to call the current state to object command. This command rebuild the cache for the passed object if needed, and then clone the polygonal cache object for you.

                                      1 Reply Last reply Reply Quote 0
                                      • H
                                        Helper
                                        last edited by

                                        On 10/04/2015 at 08:05, xxxxxxxx wrote:

                                        Hi Yannick,

                                        testing further I figured out that this not an update problem.

                                        It´s a problem with what we a getting back from the generators link field.

                                        Getting the cache with link.GetCache() from a parametric object within a linkfield
                                        produces a polygon object within a python tag and a null object with every face as a child inside a generator object.

                                        That seems like a buggy behaviour to me.

                                        Could you please give it a try and confirm or counter my assumption?

                                        Best wishes
                                        Martin

                                        1 Reply Last reply Reply Quote 0
                                        • H
                                          Helper
                                          last edited by

                                          On 10/04/2015 at 09:51, xxxxxxxx wrote:

                                          Originally posted by xxxxxxxx

                                          testing further I figured out that this not an update problem.

                                          It´s a problem with what we a getting back from the generators link field.

                                          Getting the cache with link.GetCache() from a parametric object within a linkfield
                                          produces a polygon object within a python tag and a null object with every face as a child inside a generator object.

                                          That seems like a buggy behaviour to me.

                                          Could you please give it a try and confirm or counter my assumption?

                                          I can't reproduce this strange behavior.
                                          Have you tried the current state to object modelling command solution I explained in my previous post? It seems to work fine in all situations.

                                          Also, please start a new topic if you have different questions than the initial one. This make it easier to follow the topic and search the forum.

                                          1 Reply Last reply Reply Quote 0
                                          • H
                                            Helper
                                            last edited by

                                            On 10/04/2015 at 13:44, xxxxxxxx wrote:

                                            The strange behaviour was gone after I started a new Cinema 4d session and built up a new scene.
                                            Maybe a corruption in between development...

                                            Yannick, I tried your current state to object suggestion, but then I won´t get a valid deform cache from the object nor the deformed polygon object.
                                            With [c4d.MDATA_CURRENTSTATETOOBJECT_NOGENERATE] set to False I should get a deformed object from current state to object.

                                            Could you please post your solution?
                                            I´m a little lost at the moment...
                                            This thread is really not solved for me , sorry

                                            Best wishes
                                            Martin

                                            1 Reply Last reply Reply Quote 0
                                            • First post
                                              Last post