Repository: myremylar/pygame_gui
Issue: Pasting from the clipboard on Linux raises a FileNotFoundError exception if xsel can't be found.

**Describe the bug**
On Linux, copy-pasting something into a ``pygame_gui.elements.UITextEntryLine`` element raises a FileNotFoundError exception if xsel isn't installed.

**To Reproduce**
Steps to reproduce the behaviour:
1. Don't have xsel installed.
2. Create an pygame_gui.elements.UITextEntryLine element.
3. Click it.
4. Do ctrl + v.

**Expected behaviour**
It should probably warn about a missing dependency or fail silently instead of raising an exception. Or ideally access the clipboard in a more portable way if that's feasible?

**Platform and software (please complete the following information):**
 - OS: Linux (Arch)
 - Pygame GUI version: 0.6.8
 - Pygame version: 2.1.3

**Additional context**
Desktop environment is Gnome & I'm running with Wayland.
Exception + traceback:
```
    Traceback (most recent call last):
      File "/home/user/fork/ImLvna/clangen/main.py", line 149, in <module>
        MANAGER.process_events(event)
      File "/home/user/clanenv/lib/python3.10/site-packages/pygame_gui/ui_manager.py", line 226, in process_events
        consumed_event = ui_element.process_event(event)
      File "/home/user/clanenv/lib/python3.10/site-packages/pygame_gui/elements/ui_text_entry_line.py", line 405, in process_event
        if self._process_keyboard_shortcut_event(event):
      File "/home/user/clanenv/lib/python3.10/site-packages/pygame_gui/elements/ui_text_entry_line.py", line 646, in _process_keyboard_shortcut_event
        elif self._process_paste_event(event):
      File "/home/user/clanenv/lib/python3.10/site-packages/pygame_gui/elements/ui_text_entry_line.py", line 661, in _process_paste_event
        new_text = clipboard_paste()
      File "/home/user/clanenv/lib/python3.10/site-packages/pygame_gui/core/utility.py", line 215, in clipboard_paste
        return __linux_paste()
      File "/home/user/clanenv/lib/python3.10/site-packages/pygame_gui/core/utility.py", line 169, in __linux_paste
        with subprocess.Popen(['xsel', '-b', '-o'],
      File "/usr/lib/python3.10/subprocess.py", line 971, in __init__
        self._execute_child(args, executable, preexec_fn, close_fds,
      File "/usr/lib/python3.10/subprocess.py", line 1847, in _execute_child
        raise child_exception_type(errno_num, err_msg, err_filename)
    FileNotFoundError: [Errno 2] No such file or directory: 'xsel'
```

Changed files:
pygame_gui/elements/ui_text_entry_line.py, pygame_gui/elements/ui_text_entry_box.py, pygame_gui/core/utility.py

diff --git a/pygame_gui/core/utility.py b/pygame_gui/core/utility.py
index 35899ec..e35e645 100644
--- a/pygame_gui/core/utility.py
+++ b/pygame_gui/core/utility.py
@@ -192,13 +192,16 @@ def clipboard_copy(data: str):
     :return: A platform specific copy function.
 
     """
-    current_platform = platform.system().upper()
-    if current_platform == 'WINDOWS':
-        __windows_copy(data)
-    elif current_platform == 'LINUX':
-        __linux_copy(data)
+    if pygame.vernum.major == 2 and pygame.vernum.minor >= 2:
+        pygame.scrap.put_text(data)
     else:
-        __mac_copy(data)
+        current_platform = platform.system().upper()
+        if current_platform == 'WINDOWS':
+            __windows_copy(data)
+        elif current_platform == 'LINUX':
+            __linux_copy(data)
+        else:
+            __mac_copy(data)
 
 
 def clipboard_paste():
@@ -208,13 +211,16 @@ def clipboard_paste():
     :return: A platform specific paste function.
 
     """
-    current_platform = platform.system().upper()
-    if current_platform == 'WINDOWS':
-        return __windows_paste()
-    elif current_platform == 'LINUX':
-        return __linux_paste()
+    if pygame.vernum.major == 2 and pygame.vernum.minor >= 2:
+        return pygame.scrap.get_text()
     else:
-        return __mac_paste()
+        current_platform = platform.system().upper()
+        if current_platform == 'WINDOWS':
+            return __windows_paste()
+        elif current_platform == 'LINUX':
+            return __linux_paste()
+        else:
+            return __mac_paste()
 
 
 def create_resource_path(relative_path: Union[str, Path]):
diff --git a/pygame_gui/elements/ui_text_entry_box.py b/pygame_gui/elements/ui_text_entry_box.py
index 3db473f..133c403 100644
--- a/pygame_gui/elements/ui_text_entry_box.py
+++ b/pygame_gui/elements/ui_text_entry_box.py
@@ -522,27 +522,29 @@ class UITextEntryBox(UITextBox):
         """
         consumed_event = False
         if event.key == K_v and (event.mod & KMOD_CTRL or event.mod & KMOD_META):
-            new_text = self.convert_all_line_endings_to_unix(clipboard_paste())
-
-            if abs(self.select_range[0] - self.select_range[1]) > 0:
-                low_end = min(self.select_range[0], self.select_range[1])
-                high_end = max(self.select_range[0], self.select_range[1])
-                self.html_text = self.html_text[:low_end] + new_text + self.html_text[high_end:]
-                self.set_text(self.html_text)
-                self.edit_position = low_end + len(new_text)
-                self.text_box_layout.set_cursor_position(self.edit_position)
-                self.redraw_from_text_block()
-                self.select_range = [0, 0]
-                self.cursor_has_moved_recently = True
-            elif len(new_text) > 0:
-                self.html_text = (self.html_text[:self.edit_position] +
-                                  new_text +
-                                  self.html_text[self.edit_position:])
-                self.set_text(self.html_text)
-                self.edit_position += len(new_text)
-                self.text_box_layout.set_cursor_position(self.edit_position)
-                self.redraw_from_text_block()
-                self.cursor_has_moved_recently = True
+            paste = clipboard_paste()
+            if paste is not None:
+                new_text = self.convert_all_line_endings_to_unix(clipboard_paste())
+
+                if abs(self.select_range[0] - self.select_range[1]) > 0:
+                    low_end = min(self.select_range[0], self.select_range[1])
+                    high_end = max(self.select_range[0], self.select_range[1])
+                    self.html_text = self.html_text[:low_end] + new_text + self.html_text[high_end:]
+                    self.set_text(self.html_text)
+                    self.edit_position = low_end + len(new_text)
+                    self.text_box_layout.set_cursor_position(self.edit_position)
+                    self.redraw_from_text_block()
+                    self.select_range = [0, 0]
+                    self.cursor_has_moved_recently = True
+                elif len(new_text) > 0:
+                    self.html_text = (self.html_text[:self.edit_position] +
+                                      new_text +
+                                      self.html_text[self.edit_position:])
+                    self.set_text(self.html_text)
+                    self.edit_position += len(new_text)
+                    self.text_box_layout.set_cursor_position(self.edit_position)
+                    self.redraw_from_text_block()
+                    self.cursor_has_moved_recently = True
             consumed_event = True
         return consumed_event
 
diff --git a/pygame_gui/elements/ui_text_entry_line.py b/pygame_gui/elements/ui_text_entry_line.py
index 95f2afc..77668a3 100644
--- a/pygame_gui/elements/ui_text_entry_line.py
+++ b/pygame_gui/elements/ui_text_entry_line.py
@@ -886,15 +886,18 @@ class UITextEntryLine(UIElement):
 
         """
         is_valid = True
-        if self.forbidden_characters is not None:
-            for character in text_to_validate:
-                if character in self.forbidden_characters:
-                    is_valid = False
-
-        if is_valid and self.allowed_characters is not None:
-            for character in text_to_validate:
-                if character not in self.allowed_characters:
-                    is_valid = False
+        if text_to_validate is not None:
+            if self.forbidden_characters is not None:
+                for character in text_to_validate:
+                    if character in self.forbidden_characters:
+                        is_valid = False
+
+            if is_valid and self.allowed_characters is not None:
+                for character in text_to_validate:
+                    if character not in self.allowed_characters:
+                        is_valid = False
+        else:
+            is_valid = False
 
         return is_valid