• Login
sketchucation logo sketchucation
  • Login
⚠️ Libfredo 15.4b | Minor release with bugfixes and improvements Update

Multiple dialog scope issue on the PC

Scheduled Pinned Locked Moved Developers' Forum
24 Posts 4 Posters 374 Views 4 Watching
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 Offline
    honkinberry
    last edited by 2 Apr 2013, 22:48

    I'm having an issue with WebDialogs, that is only misfiring on PC's, Mac is okay.
    Any help would be greatly appreciated!

    What I have is this:

    
    $dialog1 = nil
    class Dialog1
    	def release ()
    		if ( @dialog and @dialog.visible? )
    			@dialog.close()
    		end # if
    	end # release
    
    	def callback(data)
    		@dialog.execute_script("document.forms.control.fieldx.value = '#{data}'")
    		@dialog.execute_script("requery()")
    	end # callback
    
    	def outbound(selected)
    		goDialog2(selected,self)
    	end # outbound
    
    	def initialize()
    		@dialog = nil
    
    		begin
    			@dialog = UI;;WebDialog.new()
    			@dialog.show_modal {
    				@dialog.add_action_callback("outbound") {|d,p| outbound(p)}
    				@dialog.execute_script("requery()")
    			} # show
    
    		rescue SystemExit
    			# clean exit
    		end # begin
    	end # initialize
    
    end # Dialog1 Class
    
    def goDialog1 ()
    	if ( $dialog1 )
    		$dialog1.release
    	end # if
    	$dialog1 = Dialog1.new()
    end # goDialog1
    
    
    $dialog2 = nil
    class Dialog2
    	def release ()
    		if ( @dialog and @dialog.visible? )
    			@dialog.close()
    		end # if
    	end # release
    
    	def returnto1 (data)
    		@dialog.close()
    		@callingmodule.callback(data)
    	end # returnto1
    
    	def initialize (selected,callingmodule)
    		@callingmodule = callingmodule
    
    		@dialog = UI;;WebDialog.new()
    		@dialog.show_modal {
    			@dialog.add_action_callback("done") {|d,p| returnto1(p)}
    		} # show
    	end # initialize
    
    end # Dialog2 Class
    
    def goDialog2 (selected,callingmodule)
    	if ( $dialog2 )
    		$dialog2.release
    	end # if
    	$dialog2 = Dialog2.new(selected,callingmodule)
    end # goDialog2
    
    

    Full disclosure: I freely admit I could be screwing something up, not following best practices!
    But I've found this to work and be nicely stable on the Mac.
    I use a global variable for each dialog class, so that if the user clicks the same button, there isn't a second instance of the same dialog (hence the .release method).
    But the primarily dialog is dialog1, which then calls dialog2 (while dialog1 is still up and displayed).
    User makes a selection in dialog2, which then closes, and dialog1 is updated with what they chose.
    So I accomplish this by passing "self" to dialog2, which can then use that to pass back the selection.

    So hopefully clearer still, the call path would be:
    goDialog1, Dialog1::initialize, Dialog1::outbound,
    goDialog2, Dialog2::initialize, Dialog2::returnto1, Dialog1::callback
    right?
    only, no. On the PC, it all works great, the first time.
    After the first time, all the calls to @dialog for dialog1 just fail.
    So if the user clicks again, outbound still fires, dialog2 still comes up, and user is still able to make a selection,
    and Dialog1::callback still fires. But inside Dialog1::callback, anything involving @dialog just stops cold, no error message, no value returned, nothing.

    Hopefully makes sense?
    Thanks again for any help!

    --J

    1 Reply Last reply Reply Quote 0
    • D Offline
      Dan Rathbun
      last edited by 3 Apr 2013, 02:58

      Things "stop cold" because "modal" has different meanings on PC, than on Mac.

      On Mac "modal" means "always on top of the application window."

      On PC, child windows must always be on top of their owner window.
      On the PC, "modal" creates a blocking window that seizes focus, and must be closed before any other window (including it's owner window,) can receive focus.

      The API dictionary should say his, doesn't it ?

      What we usually do it this:
      RUBY_PLATFORM =~ /(darwin)/i ? @dialog.show_modal() : @dialog.show()

      Also.. last I knew the block arguments for the two show methods (and the set_on_close method,) did not work on the Mac, but do on the PC.

      💭

      I'm not here much anymore.

      1 Reply Last reply Reply Quote 0
      • thomthomT Offline
        thomthom
        last edited by 3 Apr 2013, 09:15

        @honkinberry said:

        I use a global variable for each dialog class

        Convert it into an class or instance variable within your own namespace. Never any need for globals.

        Thomas Thomassen — SketchUp Monkey & Coding addict
        List of my plugins and link to the CookieWare fund

        1 Reply Last reply Reply Quote 0
        • H Offline
          honkinberry
          last edited by 3 Apr 2013, 16:08

          Thanks guys as always!
          I'll get to replacing the global variable in a minute. But for the quick change, I switched it to Modeless, but still the same issue.
          You can see that dialog2 is closing, and then calling back to dialog1.
          Yet in Dialog1.callback, the execute_script calls just fail, without any error or anything (even a execute_script("alert(0)") produces nothing. I can do a @dialog.close oddly enough, but apparently nothing else! What could be going wrong?

          
          $dialog1 = nil
          class Dialog1
             def callback(data)
                @dialog.execute_script("document.forms.control.fieldx.value = '#{data}'") # doesn't fire second time
                @dialog.execute_script("requery()") # doesn't fire second time
                # @dialog.close # this will fire!
             end # callback
          
             def outbound(selected)
                goDialog2(selected,self)
             end # outbound
          
             def initialize()
                @dialog = nil
          
                begin
                   @dialog = UI;;WebDialog.new()
                   @dialog.show {
                      @dialog.add_action_callback("outbound") {|d,p| outbound(p)}
                      @dialog.execute_script("requery()")
                   } # show
          
                rescue SystemExit
                   # clean exit
                end # begin
             end # initialize
          
          end # Dialog1 Class
          
          def goDialog1 ()
             $dialog1 = Dialog1.new()
          end # goDialog1
          
          
          $dialog2 = nil
          class Dialog2
             def returnto1 (data)
                @dialog.close()
                @callingmodule.callback(data)
             end # returnto1
          
             def initialize (selected,callingmodule)
                @callingmodule = callingmodule
          
                @dialog = UI;;WebDialog.new()
                @dialog.show_modal {
                   @dialog.add_action_callback("done") {|d,p| returnto1(p)}
                } # show
             end # initialize
          
          end # Dialog2 Class
          
          def goDialog2 (selected,callingmodule)
             $dialog2 = Dialog2.new(selected,callingmodule)
          end # goDialog2
          
          
          1 Reply Last reply Reply Quote 0
          • thomthomT Offline
            thomthom
            last edited by 3 Apr 2013, 17:26

            To enable script debugging in embedded IE you need to enable it first.

            ScriptDebugging.png

            See if that yields any JS errors.

            Btw, you have many methods with a space before the argument list - ie: method (arguments). It's something that can cause unexpected problems and I assume you're getting warnings about it.

            Thomas Thomassen — SketchUp Monkey & Coding addict
            List of my plugins and link to the CookieWare fund

            1 Reply Last reply Reply Quote 0
            • H Offline
              honkinberry
              last edited by 3 Apr 2013, 18:00

              I turned on the script debugging, still nothing.
              But I've also narrowed down to the problem, this is so strange, now I know it's something simple, must be with the HTML or something.

              But check this:

              
              class Dialog1
                 def outbound(selected)
                      @dialog.execute_script("alert(0)") # fires the first time, that's it
                      puts "firing" # fires every time
                 end # outbound
              
                 def initialize()
                    @dialog = nil
              
                    begin
                       @dialog = UI;;WebDialog.new()
                       @dialog.show {
                          @dialog.add_action_callback("outbound") {|d,p| outbound(p)}
                          @dialog.execute_script("requery()")
                       } # show
              
                    rescue SystemExit
                       # clean exit
                    end # begin
                 end # initialize
              
              end # Dialog1 Class
              
              def goDialog1 ()
                 $dialog1 = Dialog1.new()
              end # goDialog1
              
              

              When a user clicks on the button, window.location=skp fires, Ruby fires back that execute_script, I get the Javascript alert... but only once! I can keep clicking that button, and Ruby Console shows "firing", but nothing to the dialog.
              So somehow my connection back to the DOM is lost, right?
              I upgraded to IE10, thinking that might be it. Nope.
              It has to be something simple, right?

              --J

              1 Reply Last reply Reply Quote 0
              • H Offline
                honkinberry
                last edited by 3 Apr 2013, 22:35

                Ugh, I think I got it.
                With javascript:void(0) in the web dialog, everything after that point kills the Ruby link to the DOM.
                Any, uh, reason for this bug?
                I'm going to be in Boulder in a couple weeks, who's desk shall I place a doggie turd on?
                (kidding)
                (sort of)

                --J

                1 Reply Last reply Reply Quote 0
                • D Offline
                  Dan Rathbun
                  last edited by 4 Apr 2013, 03:38

                  I am under the impression that javascript %(#8000BF)[**alert()**] (at least under MSIE js,) must be called from WITHIN a javascript function.

                  So @dialog.execute_script("alert(0)") should not work.

                  BUT .. if you had a javascript function in the HTML file:

                  <script>
                    function mbox(arg) {
                      alert(arg);
                    }
                  </script>
                  
                  

                  then from Ruby, calling:
                  @dialog.execute_script("mbox('Ruby method outbound called.');")
                  might work better.

                  But of course you can use Ruby-side messagebox from within the outbound() method, thus:
                  UI.messagebox('Ruby method outbound called.')

                  ❓

                  I'm not here much anymore.

                  1 Reply Last reply Reply Quote 0
                  • D Offline
                    Dan Rathbun
                    last edited by 4 Apr 2013, 04:09

                    What are you attempting to do in simple steps (psuedocode or outline form) ?

                    Why do you need to complicate what you are doing by creating a "wrapper" class, that wraps an UI::WebDialog instance inside it ?

                    Are you trying to create a dialog singleton class ?

                    I'm not here much anymore.

                    1 Reply Last reply Reply Quote 0
                    • thomthomT Offline
                      thomthom
                      last edited by 4 Apr 2013, 07:37

                      @dan rathbun said:

                      I am under the impression that javascript %(#8000BF)[**alert()**] (at least under MSIE js,) must be called from WITHIN a javascript function.

                      I've not had problems calling alert. Don't see why it should have to be wrapped within a function.

                      alert.png

                      Also, this works:

                      alert-0.png

                      Something else seem to be amiss.

                      Thomas Thomassen — SketchUp Monkey & Coding addict
                      List of my plugins and link to the CookieWare fund

                      1 Reply Last reply Reply Quote 0
                      • H Offline
                        honkinberry
                        last edited by 4 Apr 2013, 17:22

                        I got it to work, the issue is the "javascript:void(0)"

                        As for why I'm wrapping a dialog in a class, it's for Mac compatibility (and generally seems a good idea). But since they are modeless on the Mac, it's the only way I could come up with nicely handling the dialog box instances and communication between them. Also I generally try to Class everything, whether Ruby or PHP or Javascript.

                        As for the alert calls, ThomThom is right. Not only can you call "alert(0)" just about anywhere in any method, it's the fastest and easiest way I know of testing Javascript and Ruby->Javascript communication. Particularly as this is the issue at hand, thank god I don't even have to pass a string to Javascript alert, it's just totally my BFF in that manner.

                        But back to the issue at hand, if you have this in a WebDialog html:

                        
                        <a href="javascript&#058;void(0)" onclick="window.location='skp;done'">Click Me!</a>
                        
                        

                        That works on a Mac, but on a PC, scope returns to Ruby, but Ruby is unable to access any dialog box elements, the DOM is completely gone. The only thing I've been able to do with the dialog is to close it. You can keep clicking the link, and Ruby will still get focus, but get_element_value and execute_script fail without any error.
                        So then this works:

                        
                        <a href="#" onclick="window.location='skp;done'">Click Me!</a>
                        
                        

                        Which is bad programming form.
                        See, I don't know, this discussion .

                        I did originally find this topic:
                        http://sketchucation.com/forums/viewtopic.php?f=180&t=25252
                        But I had read it wrong, I thought it was the lack of javascript:void(0) that was killing his.

                        So to repeat my question -- why on Earth would "javascript:void(0)" kill Ruby's access to the DOM?
                        That just seems such an egregious bug.
                        I'm going to be in Boulder in 3 weeks, so I'd like to personally thank whichever programmer is responsible for this. In a friendly way of course. 😄

                        --J

                        1 Reply Last reply Reply Quote 0
                        • thomthomT Offline
                          thomthom
                          last edited by 4 Apr 2013, 17:51

                          Instead of returning void, what if you prevent the event from bubbling further. That's what I do.

                          Also, instead of a class in a class, why not subclass?

                          I'll look further when I get home. Not at my computer to test right now.

                          Sent from my LT25i using Tapatalk 2

                          Thomas Thomassen — SketchUp Monkey & Coding addict
                          List of my plugins and link to the CookieWare fund

                          1 Reply Last reply Reply Quote 0
                          • D Offline
                            Dan Rathbun
                            last edited by 5 Apr 2013, 03:31

                            Why use a <A> element ?

                            Just make it a <DIV> element, which can also have onclick events.

                            I'm not here much anymore.

                            1 Reply Last reply Reply Quote 0
                            • H Offline
                              honkinberry
                              last edited by 5 Apr 2013, 04:23

                              Yes, there's plenty of ways around it.
                              But there's also nothing inherently wrong with having a link with 'javascript:void(0)' as the href, is there?
                              At least, in the sense of best programming practices.
                              In fact, it is, without fail, the best recommended practice.
                              So, my question remains -- why is it that every Sketchup developer in the world has to be careful to not accidentally follow best HTML practices, instead of one programmer in Boulder correcting what is ostensibly a single errant line of code?

                              --J

                              1 Reply Last reply Reply Quote 0
                              • thomthomT Offline
                                thomthom
                                last edited by 5 Apr 2013, 07:58

                                @honkinberry said:

                                In fact, it is, without fail, the best recommended practice.

                                In terms of a JS application?

                                It's not in terms of website development - as then the href should be a working fallback in case of JS not being enabled. Which is why the onclick event should prevent bubbling.

                                None the less - that's straying besides the point.

                                Can you provide a complete working sample where your callbacks fail?

                                Thomas Thomassen — SketchUp Monkey & Coding addict
                                List of my plugins and link to the CookieWare fund

                                1 Reply Last reply Reply Quote 0
                                • thomthomT Offline
                                  thomthom
                                  last edited by 5 Apr 2013, 08:18

                                  @honkinberry said:

                                  But back to the issue at hand, if you have this in a WebDialog html:

                                  
                                  > <a href="javascript&#058;void(0)" onclick="window.location='skp;done'">Click Me!</a>
                                  > 
                                  

                                  That works on a Mac, but on a PC, scope returns to Ruby, but Ruby is unable to access any dialog box elements, the DOM is completely gone. The only thing I've been able to do with the dialog is to close it. You can keep clicking the link, and Ruby will still get focus, but get_element_value and execute_script fail without any error.

                                  I cannot reproduce this. (Windows 7, IE10)

                                  wd.rb

                                  <span class="syntaxdefault"></span><span class="syntaxcomment">#&nbsp;load&nbsp;'c;/wd.rb'<br />#&nbsp;Example.test_void<br /></span><span class="syntaxdefault">module&nbsp;Example<br />&nbsp;&nbsp;def&nbsp;self</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">test_void<br />&nbsp;&nbsp;&nbsp;&nbsp;path&nbsp;</span><span class="syntaxkeyword">=&nbsp;</span><span class="syntaxdefault">File</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">dirname</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">__FILE__</span><span class="syntaxkeyword">)<br />&nbsp;&nbsp;&nbsp;&nbsp;@</span><span class="syntaxdefault">dialog&nbsp;</span><span class="syntaxkeyword">=&nbsp;</span><span class="syntaxdefault">UI</span><span class="syntaxkeyword">;;</span><span class="syntaxdefault">WebDialog</span><span class="syntaxkeyword">.new()<br />&nbsp;&nbsp;&nbsp;&nbsp;@</span><span class="syntaxdefault">dialog</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">set_file</span><span class="syntaxkeyword">(&nbsp;</span><span class="syntaxdefault">File</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">join</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">path</span><span class="syntaxkeyword">,&nbsp;</span><span class="syntaxstring">'sample.html'</span><span class="syntaxkeyword">)&nbsp;)<br />&nbsp;&nbsp;&nbsp;&nbsp;@</span><span class="syntaxdefault">dialog</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">show&nbsp;</span><span class="syntaxkeyword">{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@</span><span class="syntaxdefault">dialog</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">add_action_callback</span><span class="syntaxkeyword">(</span><span class="syntaxstring">"done"</span><span class="syntaxkeyword">)&nbsp;{&nbsp;|</span><span class="syntaxdefault">wd</span><span class="syntaxkeyword">,&nbsp;</span><span class="syntaxdefault">param</span><span class="syntaxkeyword">|&nbsp;</span><span class="syntaxdefault">p&nbsp;param&nbsp;</span><span class="syntaxkeyword">}<br />&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span class="syntaxcomment">#&nbsp;show<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="syntaxkeyword">@</span><span class="syntaxdefault">dialog<br />&nbsp;&nbsp;end<br />end</span>
                                  

                                  sample.html

                                  <span class="syntaxdefault"></span><span class="syntaxkeyword"><!</span><span class="syntaxdefault">DOCTYPE&nbsp;html</span><span class="syntaxkeyword">><br /><</span><span class="syntaxdefault">html</span><span class="syntaxkeyword">><br /><</span><span class="syntaxdefault">body</span><span class="syntaxkeyword">><br /><br /><</span><span class="syntaxdefault">p</span><span class="syntaxkeyword">><br />&nbsp;&nbsp;<</span><span class="syntaxdefault">a&nbsp;href</span><span class="syntaxkeyword">=</span><span class="syntaxstring">"javascript;void(0)"&nbsp;</span><span class="syntaxdefault">onclick</span><span class="syntaxkeyword">=</span><span class="syntaxstring">"window.location='skp;done@HelloWorld'"</span><span class="syntaxkeyword">></span><span class="syntaxdefault">Click&nbsp;Me</span><span class="syntaxkeyword">!</</span><span class="syntaxdefault">a</span><span class="syntaxkeyword">><br /></</span><span class="syntaxdefault">p</span><span class="syntaxkeyword">><br /><br /><</span><span class="syntaxdefault">p</span><span class="syntaxkeyword">><br />&nbsp;&nbsp;<</span><span class="syntaxdefault">a&nbsp;href</span><span class="syntaxkeyword">=</span><span class="syntaxstring">"#"&nbsp;</span><span class="syntaxdefault">onclick</span><span class="syntaxkeyword">=</span><span class="syntaxstring">"alert(document.body.innerHTML)"</span><span class="syntaxkeyword">></span><span class="syntaxdefault">innerHtml</span><span class="syntaxkeyword"></</span><span class="syntaxdefault">a</span><span class="syntaxkeyword">><br /></</span><span class="syntaxdefault">p</span><span class="syntaxkeyword">><br /><br /></</span><span class="syntaxdefault">body</span><span class="syntaxkeyword">><br /></</span><span class="syntaxdefault">html</span><span class="syntaxkeyword">>&nbsp;</span><span class="syntaxdefault"></span>
                                  

                                  2013-04-05_10h14_30.png

                                  Thomas Thomassen — SketchUp Monkey & Coding addict
                                  List of my plugins and link to the CookieWare fund

                                  1 Reply Last reply Reply Quote 0
                                  • thomthomT Offline
                                    thomthom
                                    last edited by 5 Apr 2013, 08:19

                                    oh... wait... I didn't try get_element_value and execute_script.

                                    Thomas Thomassen — SketchUp Monkey & Coding addict
                                    List of my plugins and link to the CookieWare fund

                                    1 Reply Last reply Reply Quote 0
                                    • TIGT Offline
                                      TIG Moderator
                                      last edited by 5 Apr 2013, 08:31

                                      Have you considered setting and using an enduring @@dialog rather than the instance variable @dialog ?
                                      Untested, just a thought...

                                      TIG

                                      1 Reply Last reply Reply Quote 0
                                      • thomthomT Offline
                                        thomthom
                                        last edited by 5 Apr 2013, 08:32

                                        Ah. Now I can reproduce it.

                                        2013-04-05_10h28_21.png

                                        Very strange indeed.

                                        However, I've never seen anyone using javascript&#058;void(0) instead of preventing the event from bubbling. First time I heard of this issue.

                                        If you don't use a framwork like jQuery you have to account for old IE's weirdness:
                                        http://www.quirksmode.org/js/events_order.html#link9

                                        Thomas Thomassen — SketchUp Monkey & Coding addict
                                        List of my plugins and link to the CookieWare fund

                                        1 Reply Last reply Reply Quote 0
                                        • thomthomT Offline
                                          thomthom
                                          last edited by 5 Apr 2013, 08:37

                                          Actually, you just need to return false from the onclick event.

                                          <span class="syntaxdefault"></span><span class="syntaxkeyword"><!</span><span class="syntaxdefault">DOCTYPE html</span><span class="syntaxkeyword">><br /><</span><span class="syntaxdefault">html</span><span class="syntaxkeyword">><br /><</span><span class="syntaxdefault">body</span><span class="syntaxkeyword">><br /><br /><</span><span class="syntaxdefault">input type</span><span class="syntaxkeyword">=</span><span class="syntaxstring">"text"</span><span class="syntaxdefault"> id</span><span class="syntaxkeyword">=</span><span class="syntaxstring">"cheese"</span><span class="syntaxdefault"> value</span><span class="syntaxkeyword">=</span><span class="syntaxstring">"Foo Bar"</span><span class="syntaxdefault"> </span><span class="syntaxkeyword">><br /><br /><</span><span class="syntaxdefault">p</span><span class="syntaxkeyword">><br /></span><span class="syntaxdefault">  </span><span class="syntaxkeyword"><</span><span class="syntaxdefault">a<br />    href</span><span class="syntaxkeyword">=</span><span class="syntaxstring">"javascript;void(0)"<br /></span><span class="syntaxdefault">    onclick</span><span class="syntaxkeyword">=</span><span class="syntaxstring">"window.location='skp;done@HelloWorld'; return false;"</span><span class="syntaxkeyword">><br /></span><span class="syntaxdefault">      Click Me</span><span class="syntaxkeyword">!<br /></span><span class="syntaxdefault">    </span><span class="syntaxkeyword"></</span><span class="syntaxdefault">a</span><span class="syntaxkeyword">><br /></</span><span class="syntaxdefault">p</span><span class="syntaxkeyword">><br /><br /><</span><span class="syntaxdefault">p</span><span class="syntaxkeyword">><br /></span><span class="syntaxdefault">  </span><span class="syntaxkeyword"><</span><span class="syntaxdefault">a href</span><span class="syntaxkeyword">=</span><span class="syntaxstring">"#"</span><span class="syntaxdefault"> onclick</span><span class="syntaxkeyword">=</span><span class="syntaxstring">"alert(document.body.innerHTML)"</span><span class="syntaxkeyword">></span><span class="syntaxdefault">innerHtml</span><span class="syntaxkeyword"></</span><span class="syntaxdefault">a</span><span class="syntaxkeyword">><br /></</span><span class="syntaxdefault">p</span><span class="syntaxkeyword">><br /><br /></</span><span class="syntaxdefault">body</span><span class="syntaxkeyword">><br /></</span><span class="syntaxdefault">html</span><span class="syntaxkeyword">></span><span class="syntaxdefault"></span>
                                          

                                          2013-04-05_10h37_26.png

                                          Thomas Thomassen — SketchUp Monkey & Coding addict
                                          List of my plugins and link to the CookieWare fund

                                          1 Reply Last reply Reply Quote 0
                                          • 1
                                          • 2
                                          • 1 / 2
                                          1 / 2
                                          • First post
                                            1/24
                                            Last post
                                          Buy SketchPlus
                                          Buy SUbD
                                          Buy WrapR
                                          Buy eBook
                                          Buy Modelur
                                          Buy Vertex Tools
                                          Buy SketchCuisine
                                          Buy FormFonts

                                          Advertisement