[Home] [Downloads] [Search] [Help/forum]

Gammon Forum

See www.mushclient.com/spam for dealing with forum spam. Please read the MUSHclient FAQ!

[Folder]  Entire forum
-> [Folder]  MUSHclient
. -> [Folder]  Development
. . -> [Subject]  Input Wrap
Home  |  Users  |  Search  |  FAQ
Username:
Register forum user name
Password:
Forgotten password?

Input Wrap

It is now over 60 days since the last post. This thread is closed.     [Refresh] Refresh page


Pages: 1 2  

Posted by Neva   USA  (117 posts)  [Biography] bio
Date Thu 01 Dec 2011 03:21 AM (UTC)
Message
The Lord helps those who something something something.

It works! With a config option, although I'm not even gonna get into trying to mess with making it visible.

So, I guess a couple questions follow:

1. *Now* can I have this added to the real program, since I wrote it? ;)

2. Er, if so, how?

I have not even the faintest idea how to use Git. I am very new to this sort of thing. (And frankly somewhat amazed that this works, but it does.)

Glancing at it, there are changes in: doc.h, doc.cpp, mushview.cpp, sendvw.h, sendvw.cpp, and scriptingoptions.cpp. Most of them aren't long; it's basically just one function, called four different possible places (initially, whenever the script option is turned on/off, on window size change, and on changes in the input window out of admitted laziness regarding thinking of everything else that might result in different margins).

Right now, it just uses the output window's wrap column/offset. It defaults to off, although it does now add a left/right text offset the same as the output's to match regardless of whether it's turned on or not. That could be changed. It would be pretty easy to give it its own wrap column and offset, but I didn't want to mess with that as long as the settings weren't going to be showing up on the config screen.
[Go to top] top

Posted by Fiendish   USA  (1,641 posts)  [Biography] bio   Global Moderator
Date Reply #1 on Thu 01 Dec 2011 03:28 AM (UTC)
Message
Historically I've just posted diffs here. Nick can evaluate and upload if he thinks your changes are worthwhile.

https://github.com/fiendish/aardwolfclientpackage
[Go to top] top

Posted by Neva   USA  (117 posts)  [Biography] bio
Date Reply #2 on Thu 01 Dec 2011 04:23 AM (UTC)
Message
In that case, then, I hope this works. Part I:


--- doc.cpp	Mon Jan 19 17:26:36 1970
+++ doc.cpp	Mon Jan 19 17:26:36 1970
@@ -7822,20 +7822,4 @@
   sort (m_MiniWindowsOrder.begin (), m_MiniWindowsOrder.end (), lessWindow); 
 
   }   // end of CMUSHclientDoc::SortWindows
-void CMUSHclientDoc::FixInputWrap()
-{
 
-	  for(POSITION pos=GetFirstViewPosition();pos!=NULL;)
-	  {
-	  CView* pView = GetNextView(pos);
-	  
-	  if (pView->IsKindOf(RUNTIME_CLASS(CSendView)))
-  	  {
-		  CSendView* pmyView = (CSendView*)pView;
-
-		  CEdit& theEdit = pmyView->GetEditCtrl();
-
-				pmyView->UpdateWrap();
-}	
-
-	  }}
--- doc.h	Mon Jan 19 17:26:36 1970
+++ doc.h	Mon Jan 19 17:26:36 1970
@@ -371,7 +371,6 @@
 #define OPT_FIX_OUTPUT_BUFFER   0x000800    // if changed, rework output buffer size
 #define OPT_FIX_WRAP_COLUMN     0x001000    // if changed, wrap column has changed
 #define OPT_FIX_SPEEDWALK_DELAY 0x002000    // if changed, speedwalk delay has changed
-#define OPT_FIX_INPUT_WRAP      0x003000    // Added for input wrapping.
 #define OPT_USE_MXP             0x004000    // if changed, use_mxp has changed
 #define OPT_PLUGIN_CANNOT_READ  0x100000    // plugin may not read its value
 #define OPT_PLUGIN_CANNOT_WRITE 0x200000    // plugin may not write its value
@@ -881,9 +880,6 @@
   unsigned short m_bTreeviewAliases;          // show aliases in tree view?
   unsigned short m_bTreeviewTimers;           // show timers in tree view?
 
-  // Input window wrap
-  unsigned short m_bAutoWrapInput;			// Match input wrap to output?
-
   // end of stuff saved to disk **************************************************************
 
   // stuff from pre version 11, read from disk but not saved
@@ -1427,8 +1423,6 @@
                            CArgumentList & ArgumentList);
 
   void AddToCommandHistory (LPCTSTR Message);
-
-  void FixInputWrap();
 
   // simple test to see if we are in secure mode right now
   inline bool MXP_Secure (void)
--- mushview.cpp	Mon Jan 19 17:26:36 1970
+++ mushview.cpp	Mon Jan 19 17:26:36 1970
@@ -3213,7 +3213,6 @@
   pDoc->SendWindowSizes (pDoc->m_nWrapColumn);
   EnableScrollBarCtrl (SB_VERT, pDoc->m_bScrollBarWanted);
 
-  m_bottomview->UpdateWrap();
 }
 
 void CMUSHView::OnDisplayFreezeoutput() 
--- scriptingoptions.cpp	Mon Jan 19 17:26:36 1970
+++ scriptingoptions.cpp	Mon Jan 19 17:26:36 1970
@@ -205,10 +205,10 @@
 {"utf_8",                               false,  O(m_bUTF_8)},                    
 {"validate_incoming_chat_calls",        false,  O(m_bValidateIncomingCalls)},               
 {"warn_if_scripting_inactive",          true,  O(m_bWarnIfScriptingInactive)},            
-{"wrap",                                true,  O(m_wrap)},
-{"wrap_input",							false, O(m_bAutoWrapInput, 0, 0, OPT_FIX_INPUT_WRAP)}, // Added this, also fix wrap on column change.
-{"wrap_column",                         80,    O(m_nWrapColumn), 20, MAX_LINE_WIDTH, OPT_FIX_WRAP_COLUMN | OPT_FIX_INPUT_WRAP},         
+{"wrap",                                true,  O(m_wrap)},             
+{"wrap_column",                         80,    O(m_nWrapColumn), 20, MAX_LINE_WIDTH, OPT_FIX_WRAP_COLUMN},         
 {"write_world_name_to_log",             true,  O(m_bWriteWorldNameToLog)},  
+
 {NULL}   // end of table marker            
 
   };  // end of OptionsTable 
@@ -517,11 +517,6 @@
     if (m_pCurrentLine)     // a new world might not have a line yet
       FixUpOutputBuffer (Value);
 
-  if (OptionsTable [iItem].iFlags & OPT_FIX_INPUT_WRAP)
-    {
-
-	  FixInputWrap();
-	}
   if (OptionsTable [iItem].iFlags & OPT_FIX_WRAP_COLUMN)
     {
     if (m_pCurrentLine)     // a new world might not have a line yet
[Go to top] top

Posted by Neva   USA  (117 posts)  [Biography] bio
Date Reply #3 on Thu 01 Dec 2011 04:23 AM (UTC)
Message
And Part II:


--- sendvw.cpp	Mon Jan 19 17:26:36 1970
+++ sendvw.cpp	Mon Jan 19 17:26:36 1970
@@ -1174,10 +1174,6 @@
 
   NotifyPluginCommandChanged ();
 
-  CString strCommand = GetText (GetEditCtrl());
-
-  UpdateWrap();
-
 }   // end of CSendView::OnChange
 
 
@@ -1225,86 +1221,8 @@
 
   // if they want auto-command size, put back to 1
   AdjustCommandWindowSize ();
-  
-  UpdateWrap();
-
 }   // end of CSendView::OnInitialUpdate
 
-void CSendView::UpdateWrap() {
-
-  CMUSHclientDoc* pDoc = GetDocument();
-  CEdit& theEdit = GetEditCtrl();
-
-  int iOffset = pDoc->m_iPixelOffset; // Need this either way.
-
-if (pDoc->m_bAutoWrapInput) { // If we're wrapping input, let's get to it.
-
-  	CRect r; 
-	GetClientRect(r);
-  
-	int iWidth = pDoc->m_nWrapColumn + 1;
-
-	if (iWidth < 20)
-      iWidth = 20;
-    if (iWidth > MAX_LINE_WIDTH)
-      iWidth = MAX_LINE_WIDTH;
-
-	int rMargin = r.Width() - (iWidth * pDoc->m_InputFontWidth);
-
-	// Start by figuring out what the margin should be.  If it's already that, we're not going to do anything.
-
-	if (rMargin != HIWORD(theEdit.GetMargins())){
-
-  int nStartChar;
-  int nEndChar;
-  theEdit.GetSel(nStartChar, nEndChar);	
-
-  // Get our current selection/cursor position.
-
-  CString sText;
-  theEdit.GetWindowText(sText);
-
-  // Copy current buffer.
-
-  theEdit.SetWindowText("");
-
-  // Get rid of what's currently in the window.
-
-	theEdit.SetMargins(iOffset,rMargin);
-
-	// Set margins.
-
-	// Put the text back, then finally, put cursor back where it started.
-
-	theEdit.SetWindowText(sText);
-	theEdit.SetSel (nStartChar, nEndChar); }
-} else {
-	if ( HIWORD(theEdit.GetMargins()) != iOffset) // To minimize flicker, if we're already set to no right margin, leave it that way.
-	{
-		int nStartChar;
-		int nEndChar;
-		theEdit.GetSel(nStartChar, nEndChar);	
-
-		// Get our current selection/cursor position.
-
-  CString sText;
-  theEdit.GetWindowText(sText);
-
-  // Copy current buffer.
-
-  theEdit.SetWindowText("");
-
-  // Get rid of what's currently in the window.
-
-	theEdit.SetMargins(iOffset,iOffset);
-
-	// Reset margins and put everything back where it started.
-
-	theEdit.SetWindowText(sText);
-	theEdit.SetSel (nStartChar, nEndChar); 
-	}
-}
-}
 void CSendView::OnContextMenu(CWnd*, CPoint point)
 {
 
--- sendvw.h	Mon Jan 19 17:26:36 1970
+++ sendvw.h	Mon Jan 19 17:26:36 1970
@@ -86,7 +86,6 @@
 	//{{AFX_VIRTUAL(CSendView)
 	public:
 	virtual void OnInitialUpdate();
-	virtual void UpdateWrap();
 	virtual BOOL PreTranslateMessage(MSG* pMsg);
 	protected:
 	virtual void OnDraw(CDC* pDC);      // overridden to draw this view
[Go to top] top

Posted by Twisol   USA  (2,257 posts)  [Biography] bio
Date Reply #4 on Thu 01 Dec 2011 05:57 AM (UTC)
Message
Just FYI, your diff is going backwards. ;) There are minuses where you actually added stuff, and plusses where stuff was removed.

I would test your changes out, but I don't have VS on this computer at the moment, and it's been a long while since I've hacked on the MUSHclient codebase. There's a couple points I want to raise, but otherwise I think it looks pretty good. (Especially if it works!)

1) Nick uses an older version of Visual Studio (actually VC++7 if I recall), so CEdit::SetMargins() may not be implemented in his version of MFC. Granted, MSDN says it's been available since Windows 95, but it's something to keep in mind.

2) I'm a little surprised that you need to remove the text, set the margins, then add the text back to the input area. I can't see how that's necessary, but maybe you can explain that for me. (If it doesn't display the changes otherwise, maybe try calling theEdit.Invalidate(); that should force it to update.)

'Soludra' on Achaea

Blog: http://jonathan.com/
GitHub: http://github.com/Twisol
[Go to top] top

Posted by Neva   USA  (117 posts)  [Biography] bio
Date Reply #5 on Thu 01 Dec 2011 06:54 AM (UTC)
Message
Okay, see, this diff thing is the sort of thing I have no idea about. Nor do I really know how to do it the other way... well, Nick, let me know if I need to do it again or what, when you see this. :)

As far as removing the text bit, that does work more simply, anyway. Both ways flicker on my screen; this way seems to flicker less for reasons I really don't understand. But if that way looks better for anybody else, it's hardly a big deal one way or another!
[Go to top] top

Posted by Twisol   USA  (2,257 posts)  [Biography] bio
Date Reply #6 on Thu 01 Dec 2011 09:03 AM (UTC)

Amended on Thu 01 Dec 2011 09:04 AM (UTC) by Twisol

Message
Neva said:
As far as removing the text bit, that does work more simply, anyway. Both ways flicker on my screen; this way seems to flicker less for reasons I really don't understand. But if that way looks better for anybody else, it's hardly a big deal one way or another!

Hmm. I assume UpdateWrap() should only be called when you set the wrapping option and when the control is initialized. However, according to some research, because it's called from CSendView::OnChange it's executed every time the contents of the edit box are edited. The margin should only need to be set when it's changed or initialized, so the frequent updating might be causing a flickering effect.

</theory>

'Soludra' on Achaea

Blog: http://jonathan.com/
GitHub: http://github.com/Twisol
[Go to top] top

Posted by Neva   USA  (117 posts)  [Biography] bio
Date Reply #7 on Thu 01 Dec 2011 07:53 PM (UTC)
Message
That OnChange would be the part I mentioned in the first post.

It does need to be called more often than just when the window's created or when you enable/disable because the right margin is a relative value, not an absolute value. Anything that changes the window width, font width, or wrap column will require it to be updated. If we could come up with an exhaustive list of every script option and config option which would potentially change the font and wrap, it could be hooked into all of them. I don't really know if it's worth taking the time. (The window width changes are already handled separately.)

The part that I said I'm not sure about is why one method causes more flicker than the other. But I think some more Googling has suggested some ways to deal with that, so I'm going to have another poke at it this evening.
[Go to top] top

Posted by Nick Gammon   Australia  (21,322 posts)  [Biography] bio   Forum Administrator
Date Reply #8 on Fri 02 Dec 2011 07:23 AM (UTC)
Message
Neva said:

Okay, see, this diff thing is the sort of thing I have no idea about. Nor do I really know how to do it the other way... well, Nick, let me know if I need to do it again or what, when you see this. :)



Instead of:

diff a b

do:

diff b a

Then it reverses the additions/subtractions.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] top

Posted by Neva   USA  (117 posts)  [Biography] bio
Date Reply #9 on Fri 02 Dec 2011 10:48 AM (UTC)
Message
Okay! Invalidate(FALSE) seems to solve it. Not sure why, but hey, I can roll with it.


--- doc.cpp	Mon Jan 19 17:26:36 1970
+++ doc.cpp	Mon Jan 19 17:26:36 1970
@@ -7822,4 +7822,20 @@
   sort (m_MiniWindowsOrder.begin (), m_MiniWindowsOrder.end (), lessWindow); 
 
   }   // end of CMUSHclientDoc::SortWindows
+void CMUSHclientDoc::FixInputWrap()
+{
 
+	  for(POSITION pos=GetFirstViewPosition();pos!=NULL;)
+	  {
+	  CView* pView = GetNextView(pos);
+	  
+	  if (pView->IsKindOf(RUNTIME_CLASS(CSendView)))
+  	  {
+		  CSendView* pmyView = (CSendView*)pView;
+
+		  CEdit& theEdit = pmyView->GetEditCtrl();
+
+				pmyView->UpdateWrap();
+}	
+
+	  }}
--- doc.h	Mon Jan 19 17:26:36 1970
+++ doc.h	Mon Jan 19 17:26:36 1970
@@ -371,6 +371,7 @@
 #define OPT_FIX_OUTPUT_BUFFER   0x000800    // if changed, rework output buffer size
 #define OPT_FIX_WRAP_COLUMN     0x001000    // if changed, wrap column has changed
 #define OPT_FIX_SPEEDWALK_DELAY 0x002000    // if changed, speedwalk delay has changed
+#define OPT_FIX_INPUT_WRAP      0x003000    // Added for input wrapping.
 #define OPT_USE_MXP             0x004000    // if changed, use_mxp has changed
 #define OPT_PLUGIN_CANNOT_READ  0x100000    // plugin may not read its value
 #define OPT_PLUGIN_CANNOT_WRITE 0x200000    // plugin may not write its value
@@ -880,6 +881,9 @@
   unsigned short m_bTreeviewAliases;          // show aliases in tree view?
   unsigned short m_bTreeviewTimers;           // show timers in tree view?
 
+  // Input window wrap
+  unsigned short m_bAutoWrapInput;			// Match input wrap to output?
+
   // end of stuff saved to disk **************************************************************
 
   // stuff from pre version 11, read from disk but not saved
@@ -1423,6 +1427,8 @@
                            CArgumentList & ArgumentList);
 
   void AddToCommandHistory (LPCTSTR Message);
+
+  void FixInputWrap();
 
   // simple test to see if we are in secure mode right now
   inline bool MXP_Secure (void)
--- mushview.cpp	Mon Jan 19 17:26:36 1970
+++ mushview.cpp	Mon Jan 19 17:26:36 1970
@@ -3213,6 +3213,7 @@
   pDoc->SendWindowSizes (pDoc->m_nWrapColumn);
   EnableScrollBarCtrl (SB_VERT, pDoc->m_bScrollBarWanted);
 
+  m_bottomview->UpdateWrap();
 }
 
 void CMUSHView::OnDisplayFreezeoutput() 
--- scriptingoptions.cpp	Mon Jan 19 17:26:36 1970
+++ scriptingoptions.cpp	Mon Jan 19 17:26:36 1970
@@ -205,10 +205,10 @@
 {"utf_8",                               false,  O(m_bUTF_8)},                    
 {"validate_incoming_chat_calls",        false,  O(m_bValidateIncomingCalls)},               
 {"warn_if_scripting_inactive",          true,  O(m_bWarnIfScriptingInactive)},            
-{"wrap",                                true,  O(m_wrap)},             
-{"wrap_column",                         80,    O(m_nWrapColumn), 20, MAX_LINE_WIDTH, OPT_FIX_WRAP_COLUMN},         
+{"wrap",                                true,  O(m_wrap)},
+{"wrap_input",							false, O(m_bAutoWrapInput, 0, 0, OPT_FIX_INPUT_WRAP)}, // Added this, also fix wrap on column change.
+{"wrap_column",                         80,    O(m_nWrapColumn), 20, MAX_LINE_WIDTH, OPT_FIX_WRAP_COLUMN | OPT_FIX_INPUT_WRAP},         
 {"write_world_name_to_log",             true,  O(m_bWriteWorldNameToLog)},  
-
 {NULL}   // end of table marker            
 
   };  // end of OptionsTable 
@@ -517,6 +517,11 @@
     if (m_pCurrentLine)     // a new world might not have a line yet
       FixUpOutputBuffer (Value);
 
+  if (OptionsTable [iItem].iFlags & OPT_FIX_INPUT_WRAP)
+    {
+
+	  FixInputWrap();
+	}
   if (OptionsTable [iItem].iFlags & OPT_FIX_WRAP_COLUMN)
     {
     if (m_pCurrentLine)     // a new world might not have a line yet
--- sendvw.cpp	Mon Jan 19 17:26:36 1970
+++ sendvw.cpp	Mon Jan 19 17:26:36 1970
@@ -1174,6 +1174,10 @@
 
   NotifyPluginCommandChanged ();
 
+  CString strCommand = GetText (GetEditCtrl());
+
+  UpdateWrap();
+
 }   // end of CSendView::OnChange
 
 
@@ -1221,8 +1225,54 @@
 
   // if they want auto-command size, put back to 1
   AdjustCommandWindowSize ();
+  
+  UpdateWrap();
+
 }   // end of CSendView::OnInitialUpdate
 
+void CSendView::UpdateWrap() {
+
+  CMUSHclientDoc* pDoc = GetDocument();
+  CEdit& theEdit = GetEditCtrl();
+
+  int iOffset = pDoc->m_iPixelOffset; // Need this either way.
+
+if (pDoc->m_bAutoWrapInput) {
+
+  	CRect r; 
+	GetClientRect(r); // How big is the input window?
+  
+	int iWidth = pDoc->m_nWrapColumn + 1;  // Letter-width of the line.
+
+	if (iWidth < 20)
+      iWidth = 20;
+    if (iWidth > MAX_LINE_WIDTH)
+      iWidth = MAX_LINE_WIDTH; // Uses the minimums and maximums from elsewhere.
+
+	// Subtracts the pixel width of text and the left-side offset to determine what the right side should be.
+	int rMargin = r.Width() - (iWidth * pDoc->m_InputFontWidth) - iOffset;
+
+	// If the right side isn't at least as big as the offset, use the offset.
+	if (rMargin < iOffset)
+		rMargin = iOffset;
+
+	// If the margin needs to change, change it.
+	if (rMargin != HIWORD(theEdit.GetMargins()))
+	{
+	theEdit.SetMargins(iOffset,rMargin);
+	theEdit.Invalidate(FALSE); 
+	}
+
+} else {
+
+	if ( HIWORD(theEdit.GetMargins()) != iOffset) // If the option is off, just using the offset.
+	{
+	theEdit.SetMargins(iOffset,iOffset);
+	theEdit.Invalidate(FALSE);
+	}
+}
+	
+}
 void CSendView::OnContextMenu(CWnd*, CPoint point)
 {
 
--- sendvw.h	Mon Jan 19 17:26:36 1970
+++ sendvw.h	Mon Jan 19 17:26:36 1970
@@ -86,6 +86,7 @@
 	//{{AFX_VIRTUAL(CSendView)
 	public:
 	virtual void OnInitialUpdate();
+	virtual void UpdateWrap();
 	virtual BOOL PreTranslateMessage(MSG* pMsg);
 	protected:
 	virtual void OnDraw(CDC* pDC);      // overridden to draw this view
[Go to top] top

Posted by Nick Gammon   Australia  (21,322 posts)  [Biography] bio   Forum Administrator
Date Reply #10 on Sat 03 Dec 2011 09:03 AM (UTC)
Message
Very good. I'll incorporate your patch into the next version, subject to testing etc. etc.

Remind me in a week if I haven't said anything, I am in rather chaotic changes to my house here. :)

- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] top

Posted by Neva   USA  (117 posts)  [Biography] bio
Date Reply #11 on Sat 03 Dec 2011 10:51 AM (UTC)
Message
Who says you're allowed to have a real life! *flails*

Actually, aside from the fact that I think this is something other people could conceivably like, the lovely bit of this is no longer having to wait for a new version!
[Go to top] top

Posted by Twisol   USA  (2,257 posts)  [Biography] bio
Date Reply #12 on Sat 03 Dec 2011 03:45 PM (UTC)
Message
Hooray for open-source! :)

'Soludra' on Achaea

Blog: http://jonathan.com/
GitHub: http://github.com/Twisol
[Go to top] top

Posted by Neva   USA  (117 posts)  [Biography] bio
Date Reply #13 on Sat 03 Dec 2011 04:51 PM (UTC)
Message
I'm sort of shocked by how easy this is, but I guess I shouldn't be. This is how I learned MUSHcode, by keeping all the helpfiles nearby and doing a lot of trial-and-error.

I'm now poking at shortening up that awful-looking 8-character tab (and yes I know it's standard, that's why I'm just doing it for me) and possibly turning off the auto-sensing softcode comment thing, because I've never used @@comments in my own code, the only package I have that uses them isn't formatted, and I keep pasting stuff in from the middle of bits and ending up with '# stuff' lines inserted in the middle... although I have a half-written Lua update that I may finish instead one of these days.

Oh, I'm sure this is getting off the subject for this thread, but I'm curious, what version of this lrexlib.c is this that doesn't have gsub and stuff? It doesn't really resemble the one that's up now, has it just changed that much since it was incorporated?
[Go to top] top

Posted by Nick Gammon   Australia  (21,322 posts)  [Biography] bio   Forum Administrator
Date Reply #14 on Mon 12 Dec 2011 07:40 PM (UTC)
Message
An old one. I don't always keep checking for updates on the more obscure libraries I have incorporated (eg. the big number library).

- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] top

The dates and times for posts above are shown in Universal Co-ordinated Time (UTC).

To show them in your local time you can join the forum, and then set the 'time correction' field in your profile to the number of hours difference between your location and UTC time.


14,149 views.

This is page 1, subject is 2 pages long: 1 2  [Next page]

It is now over 60 days since the last post. This thread is closed.     [Refresh] Refresh page

Go to topic:           Search the forum


[Go to top] top

Quick links: MUSHclient. MUSHclient help. Forum shortcuts. Posting templates. Lua modules. Lua documentation.

Information and images on this site are licensed under the Creative Commons Attribution 3.0 Australia License unless stated otherwise.

[Home]


Written by Nick Gammon - 5K   profile for Nick Gammon on Stack Exchange, a network of free, community-driven Q&A sites   Marriage equality

Comments to: Gammon Software support
[RH click to get RSS URL] Forum RSS feed ( https://gammon.com.au/rss/forum.xml )

[Best viewed with any browser - 2K]    [Hosted at FutureQuest]