#!/usr/bin/ruby -w
# Encoding: UTF-8
# frozen_string_literal: true
# =========================================================================== #
# This file contains some custom code for ruby-gtk.
#
# You can use code in this file to enable or disable warnings.
#
# Usage examples:
#
#   Gtk.enable_warnings
#   Gtk.disable_warnings
#
# =========================================================================== #
# require 'gtk_paradise/toplevel_methods/toplevel_methods.rb'
# Gtk.default_base_module
# ::Gtk.spacer
# Gtk.return_editable_cell_renderer_text
# Gtk.register_sigint
# =========================================================================== #
module Gtk

  require 'gtk_paradise/toplevel_methods/determine_which_gtk_version_is_in_use.rb'
  require 'gtk_paradise/toplevel_methods/css.rb'
  require 'gtk_paradise/toplevel_methods/events.rb'
  require 'gtk_paradise/toplevel_methods/keys.rb'

  # ========================================================================= #
  # === @old_verbose_value
  #
  # This can be either true or false.
  # ========================================================================= #
  @old_verbose_value = $VERBOSE # Keep a "reference" to the old value.

  # ========================================================================= #
  # === Gtk.old_verbose_value?
  # ========================================================================= #
  def self.old_verbose_value?
    return @old_verbose_value
  end

  # ========================================================================= #
  # === Gtk.reset_the_grid_counter
  # ========================================================================= #
  def self.reset_the_grid_counter
    ::Gtk::Grid.reset
  end

  # ========================================================================= #
  # === Gtk.custom_treeview_widget
  #
  # This method can be used to designate a custom treeview widget, in
  # particular an icon, and then a normal gtk-label.
  #
  # The first argument must be the TreeView whose column we which to modify.
  #
  # The second argument shall tell us which column to modify in that
  # treeview.
  # ========================================================================= #
  def self.custom_treeview_widget(
      this_treeview_widget,
      position                   = 0,
      location_to_the_image_file = :sword,
      title_for_the_label        = '#Attacks',
      position_of_the_image      = :to_the_left,
      use_this_width             = 16,
      use_this_height            = 16
    )
    column = this_treeview_widget.get_column(position) # Obtain a handle on it.
    box = Gtk.hbox
    begin
      require 'cyberweb/requires/require_web_images.rb'
      if location_to_the_image_file.is_a? Symbol
        location_to_the_image_file = Cyberweb.image_url(
          location_to_the_image_file
        ) { :prepend_the_image_base_directory }
      end
    rescue LoadError => error
      if ::Gtk.is_on_roebe?
        pp error
        puts 'Please fix this ^^^ error. '\
             '(Error class: '+error.class.to_s+')'
      end
    end
    pixbuf_image = GdkPixbuf::Pixbuf.new(
      file:   location_to_the_image_file.to_s,
      width:  use_this_width,
      height: use_this_height
    )
    # if ::Gtk.use_gtk2?
    #  icon = ::Gtk::Image.new(pixbuf_image)
    icon = ::Gtk::Image.new(pixbuf: pixbuf_image)
    label = ::Gtk::Label.new(title_for_the_label)

    # ======================================================================= #
    # We put the image either on the left or on the right side.
    # ======================================================================= #
    case position_of_the_image
    # ======================================================================= #
    # === :to_the_left
    # ======================================================================= #
    when :to_the_left
      box.add(icon)
      box.add(label)
    else # Else we put it on the right side.
      box.add(label)
      box.add(icon)
    end
    box.show_all
    column.widget = box
  end

  # ========================================================================= #
  # === Gtk.horizontal_spacer
  #
  # We have to check whether gtk2 or gtk3 is used. Depending on this
  # we will return a "different" separator.
  # ========================================================================= #
  def self.horizontal_spacer
    # if is_gtk2_in_use?
    #   ::Gtk::HSeparator.new
    # Assume gtk3 and gtk4 here.
    ::Gtk::Separator.new(:horizontal)
  end; self.instance_eval { alias hspacer horizontal_spacer } # === Gtk.hspacer

  # ========================================================================= #
  # === Gtk.return_n_characters_up_to_array_position
  #
  # We follow simple array-logic here, so the given input argument, the
  # first one, is using the same logic as Arrays in general: the counting
  # starts at 0.
  #
  # The method was specifically added because a "goto-this-line"
  # functionality was needed for a gtk-based editor. 
  # ========================================================================= #
  def self.return_n_characters_up_to_array_position(
      i     = 3,
      array = [
        "This is some text.\n",               # 0
        "class Foobar # this is a comment\n", # 1
        "end\n",                              # 2
        "AHIFOUVEUHIVE\n",                    # 3
        "underlined!!!\n",                    # 4
        "bold text example!!!\n"              # 5
      ]
    )
    position = 0
    # ======================================================================= #
    # We must re-adjust i if the Array does not have enough elements. We
    # will deduct -1 lateron anyway, so the following code is correct.
    # ======================================================================= #
    if i > array.size
      i = array.size
    end
    0.upto(i).each {|entry_number|
      if entry_number == 0
      else # else calculate the line BEFORE.
        position += array[entry_number - 1].size
      end
    }
    return position
  end

  # ========================================================================= #
  # === Gtk.set_double_click_time
  #
  # This method sets the double click time for the default display.
  # Applications should NOT set this, as it is a global user-configured
  # setting.
  # ========================================================================= #
  def self.set_double_click_time(msec = 50)
    Gdk.set_double_click_time(msec) # Delegate towards Gdk.
  end

  # ========================================================================= #
  # === Gtk.textview_with_text
  #
  # This method can be used to create a textview-widget.
  # ========================================================================= #
  def self.textview_with_text(use_this_text = '')
    textview_widget = ::Gtk.textview
    textview_widget.set_text(use_this_text)
    # textview.cursor_visible = false # Hide the cursor here.
    return textview_widget 
  end

  # ========================================================================= #
  # === Gtk.make_shortcut_key_for_gtk_entry
  #
  # This method will "make" a shortcut key.
  #
  # Arguments to this method:
  #
  #   - main_window is the main window which covers all your sub-windows.
  #     It should be of Gtk::Window type.
  #
  # Usage example:
  #
  #   Gtk.make_shortcut_key_for_gtk_entry(@main_window, @accel_group, '1', @entry)
  #
  # ========================================================================= #
  def self.make_shortcut_key_for_gtk_entry(
      main_window, 
      accel_group,
      shortcut_key = 'b',
      the_entry    = ''
    )
    accel_group.connect(
      Gdk::Keyval.from_name(shortcut_key.to_s),
      Gdk::Window::MOD1_MASK,
      :visible
    ) { set_focus_and_select(the_entry) }
    main_window.add_accel_group(accel_group).show_all
  end

  # ========================================================================= #
  # === Gtk.disable_warnings
  # ========================================================================= #
  def self.disable_warnings
    @old_verbose_value = $VERBOSE # Must keep a reference.
    $VERBOSE = nil # And now we silence it.
  end

  # ========================================================================= #
  # === Gtk.revert_to_the_old_warnings
  # ========================================================================= #
  def self.revert_to_the_old_warnings
    $VERBOSE = @old_verbose_value
  end

  # ========================================================================= #
  # === Gtk.left_aligned_text
  # ========================================================================= #  
  def self.left_aligned_text(i = nil)
    _ = text(i)
    _.left_align
    return _
  end

  # ========================================================================= #
  # === Gtk.return_custom_generic_widget
  #
  # Usage example:
  #
  #   x = Gtk.return_custom_generic_widget
  #
  # ========================================================================= #
  def self.return_custom_generic_widget
    require 'gtk_paradise/requires/require_the_base_module.rb'
    box = ::Gtk.vbox
    box.extend(::Gtk::BaseModule)
    return box
  end; self.instance_eval { alias return_custom_widget return_custom_generic_widget } # === Gtk.return_custom_widget
       self.instance_eval { alias default_base_module  return_custom_generic_widget } # === Gtk.default_base_module

  # ========================================================================= #
  # === Gtk.enable_warnings
  #
  # Note that the functionality in this method depends on
  # @old_verbose_value really.
  # ========================================================================= #
  def self.enable_warnings(
      i = @old_verbose_value
    )
    $VERBOSE = i
  end; self.instance_eval { alias re_enable_warnings enable_warnings } # === Gtk.re_enable_warnings

  # ========================================================================= #
  # === @main_file
  #
  # This method can be used to keep track of any file that was selected
  # via the gtk-file-picker widget. Only one file can be selected at
  # any given moment in time.
  # ========================================================================= #
  @main_file = nil

  # ========================================================================= #
  # === Gtk.set_main_file
  # ========================================================================= #
  def self.set_main_file(i)
    @main_file = i
  end

  # ========================================================================= #
  # === Gtk.main_file?
  # ========================================================================= #
  def self.main_file?
    @main_file
  end

  # ========================================================================= #
  # === Gtk.register_sigint
  # ========================================================================= #
  def self.register_sigint
    Signal.trap('SIGINT') { exit }
  end

  # ========================================================================= #
  # === Gtk.e
  #
  # This method is simply a wrapper over puts.
  # ========================================================================= #
  def self.e(i = '')
    puts i
  end

  # ========================================================================= #
  # === Gtk.is_on_roebe?
  # ========================================================================= #
  def self.is_on_roebe?(
      i = ENV['IS_ROEBE']
    )
    i.to_s == '1'
  end; self.instance_eval { alias on_roebe?        is_on_roebe? } # === Gtk.on_roebe?
       self.instance_eval { alias are_we_on_roebe? is_on_roebe? } # === Gtk.are_we_on_roebe?

  # ========================================================================= #
  # === Gtk.do_grab_the_keyboard
  #
  # This works on ruby-gtk2. It is not entirely clear whether it works
  # on ruby-gtk3 and ruby-gtk4, though.
  # ========================================================================= #
  def self.do_grab_the_keyboard(i)
    ::Gdk.keyboard_grab(
      i.window,
      true,
      Gdk::Event::CURRENT_TIME
    )
  end

  # ========================================================================= #
  # === Gtk.do_ungrab_the_keyboard
  # ========================================================================= #
  def self.do_ungrab_the_keyboard
    ::Gdk.keyboard_ungrab(Gdk::Event::CURRENT_TIME)
  end

  # ========================================================================= #
  # === Gtk.spacer
  #
  # This method will add a horizontal spacer.
  #
  # Note that the Gtk::Separator widget is an abstract class, used only 
  # for deriving the subclasses Gtk::HSeparator and Gtk::VSeparator.
  # ========================================================================= #
  def self.spacer
    # ::Gtk::Separator.new(:horizontal) # ^^^ see above for an explanation why this is commented out.
    return Gtk.horizontal_spacer
  end

  # ========================================================================= #
  # === Gtk.create_gtk_source_view
  #
  # This method will create a default Gtk::SourceView object.
  # ========================================================================= #
  def self.create_gtk_source_view(
      buffer_to_use
    )
    _ = ::Gtk::SourceView.new(buffer_to_use)
    _.show_line_numbers = true
    _.show_line_markers = true if _.respond_to? :show_line_markers
    _.smart_home_end = true
    _.tabs_width = 2
    return _
  end

  # ========================================================================= #
  # === Gtk.create_event_image
  #
  # Simple event-image. Will embed an image into an EventBox.
  # ========================================================================= #
  def self.create_event_image(
      file_url = ENV['IMG_RPG'].to_s+'/ADOPTION/WALDSZENE.jpg'
    )
    image = ::Gtk::Image.new(file_url)
    event_box = ::Gtk::EventBox.new.add(image)
    event_box.signal_connect(:button_press_event) { puts 'Clicked.' }
    return event_box
  end

  # ========================================================================= #
  # === Gtk.populate_this_liststore
  #
  # This convenience method can be used to populate a liststore
  # with data. The dataset, the second argument, should be an
  # Array. You need to make sure that the input-array is
  # correct and has the correct number of elements as well.
  #
  # Note that the old dataset will be cleared by default, as decided
  # by the third argument to this method.
  # ========================================================================= #
  def self.populate_this_liststore(
      list_store,
      dataset,
      shall_we_clear_the_liststore = true
    )
    list_store.clear if shall_we_clear_the_liststore
    if dataset.is_a? String
    end
    dataset.each { |inner_array_containing_the_dataset|
      iter = list_store.append # Set to the new iter here.
      inner_array_containing_the_dataset.each_with_index {|element, inner_index|
        # =================================================================== #
        # Next, update every iter element; inner_index keeps track of the
        # position.
        # =================================================================== #
        iter.set_value(inner_index, element)
      }
    }
  end

  # ========================================================================= #
  # === Gtk.pixbuf_new_from_file
  # ========================================================================= #
  def self.pixbuf_new_from_file(this_file)
    ::GdkPixbuf::Pixbuf.new(file: this_file)
  end; self.instance_eval { alias pixbuf_from_file pixbuf_new_from_file } # === Gtk.pixbuf_from_file
       self.instance_eval { alias image_from_file  pixbuf_new_from_file } # === Gtk.image_from_file

  # ========================================================================= #
  # === Gtk.load_icon
  # ========================================================================= #
  def self.load_icon(
      a = 'application-exit',
      b = 32,
      c =  0
    )
    ::Gtk::IconTheme.default.load_icon(a, b, c)
  end

  # ========================================================================= #
  # === Gtk.create_entry_with_text_and_max_length
  #
  # Creates a new gtk entry. The text displayed is given via the first
  # argument to this method.
  # ========================================================================= #
  def self.create_entry_with_text_and_max_length(
      text       = '',
      max_length = 50
    )
    _ = ::Gtk::Entry.new
    _.set_max_length(max_length)
    _.set_text(text)
    return _
  end

  # ========================================================================= #
  # === Gtk.set_background
  # ========================================================================= #
  def self.set_background(
      use_this_colour = :grey
    )
    use_this_colour = Gdk.colour_parse(use_this_colour)
    modify_bg(
      ::Gtk::StateType::NORMAL, use_this_colour
    )
  end; self.instance_eval { alias background= set_background } # === Gtk.background=
       self.instance_eval { alias set_bg      set_background } # === Gtk.set_bg

  # ========================================================================= #
  # === Gtk.run_a_random_gtk_class
  #
  # This functionality depends on Gtk::Runner.
  # ========================================================================= #
  def self.run_a_random_gtk_class
    runner = Gtk::Runner.new
    runner << instantiate_a_random_gtk_class
    runner.top_left_then_run
  end

  # ========================================================================= #
  # === Gtk.to_utf8
  #
  # Convert to UTF8 format here. The second argument is the encoding of
  # your inputted string. By default, this is ISO 8859-1.
  #
  # Note that this method is, past the year 2020, probably no longer as
  # important as it used to be, since I changed to Unicode (UTF-8) as
  # default encoding - but other ruby users may still use another
  # encoding, so we will leave this convenience method here.
  #
  # Basically consider it simply as a wrappre over GLib.convert().
  # ========================================================================= #
  def self.to_utf8(
      i,
      from_this_encoding = 'iso-8859-1'
    )
    return GLib.convert(i, 'utf-8', from_this_encoding)
  end; self.instance_eval { alias to_utf to_utf8 } # === Gtk.to_utf

  # ========================================================================= #
  # === Gtk.which_mouse_button
  #
  # This method serves mostly as a mnemonic-helper.
  #
  # puts event.button
  #
  # Which mouse button was pressed?
  #
  #   puts which_mouse_button(event.button)
  #
  # ========================================================================= #
  def self.which_mouse_button(
      type = :default
    )
    case type
    when 1,
         :default
      :left
    when 2
      :middle
    when 3
      :right
    else
      :unknown
    end 
  end

  # ========================================================================= #
  # === Gtk.name_of_this_event?
  #
  # This method will simply return the name of the event.
  # ========================================================================= #
  def self.name_of_this_event?(event)
    return event.event_type.name
  end

  require 'gtk_paradise/toplevel_methods/screen_resolution.rb'
  # ========================================================================= #
  # === Gtk.calculate_the_true_value_represented_via_a_percentage_value
  # ========================================================================= #
  def self.calculate_the_true_value_represented_via_a_percentage_value(
      i                       = '55% or minimum 500px',
      max_width_or_max_height = ::Gtk.max_width?
    )
    if i.is_a?(String) and i.include?('%')
      # ===================================================================== #
      # Handle something like '80%'. Since as of October 2021 this also
      # has to handle input such as '30% or minimum 300px'.
      # ===================================================================== #
      if i.include? ' '
        if i.include? 'minimum'
          splitted = i.split(' ')
          minimum_value     = splitted.last.to_s.delete('px').to_f
          assumed_new_value = splitted.first.delete('%').to_f
          assumed_new_value = (max_width_or_max_height * assumed_new_value) / 100.0
          i = assumed_new_value
          if minimum_value > assumed_new_value
            i = minimum_value
          end
        end
      else # this clause handles '50%'
        i = (max_width_or_max_height * i.delete('%').to_f) / 100.0
      end
    end
    return i
  end

  # ========================================================================= #
  # === Gtk.replace_with_proper_tags
  #
  # This method can be used to add ad-hoc colour support for HTML-like
  # tags (markup).
  #
  # The method will return a String.
  # ========================================================================= #
  def self.replace_with_proper_tags(
      i = '<green>green</green>
<blue>blue</blue>
<steelblue>steelblue</steelblue>

<green>Hello</green><span foreground="blue"> world!</span>


<steelblue>longer text</steelblue>
longer text
')
    begin
      require 'colours'
    rescue LoadError; end
    splitted = i.split("\n")
    _ = ''.dup
    html_colours = ::Colours.available_html_colours?
    splitted.each {|line|
      # ===================================================================== #
      # <steelblue>steelblue</steelblue>
      # The first part has to become a <span> tag and the last entry a
      # </span> tag.
      # ===================================================================== #
      if html_colours.any? {|inner_html_colour| line.include? '<'+inner_html_colour+'>' }
        scanned_results = html_colours.select {|inner_html_colour|
          line.include? '<'+inner_html_colour+'>'
        }
        scanned_results.each {|this_is_the_colour|
          line.gsub!(/<\/#{this_is_the_colour}>/, '</span>')
          line.gsub!(
            /<#{this_is_the_colour}>/, '<span foreground="'+this_is_the_colour.to_s+'">'
          )
        }
      end
      _ << "#{line}\n"
    }
    return _
  end

  # ========================================================================= #
  # === Gtk.read_in_from_this_csv_file
  #
  # This method will combine Gtk::ListStore.new and Gtk::TreeView.new().
  #   
  # The third argument can use symbols such as :strip_quotes.
  # ========================================================================= #
  def self.read_in_from_this_csv_file(
      i                                   = :default,
      use_this_token_for_the_split_action = :default,
      extra_commands                      = nil
    )
    require 'gtk_paradise/core_classes/tree_view.rb'
    # ======================================================================= #
    # === Handle Hash as input first
    # ======================================================================= #
    if i.is_a? Hash
      # ===================================================================== #
      # === :split_on
      # ===================================================================== #
      if i.has_key? :split_on
        use_this_token_for_the_split_action = i[:split_on]
      end
    end
    case use_this_token_for_the_split_action
    when :default
      use_this_token_for_the_split_action = ','
    end

    if i.is_a? Array
      i = i.join(' ').strip
    end
    case i
    # ======================================================================= #
    # === :default
    #
    # Make use of a default file. This is catered to my own use case.
    #
    # Simply supply your own .csv file if you want to test this
    # functionality.
    # ======================================================================= #
    when :default, nil, ''
      i = '/home/x/programming/ruby/src/cyberweb/examples/csv/sample.csv' # A test-file on my home setup.
    end
    if File.exist? i
      dataset = File.readlines(i)
      if dataset.is_a? Array
        dataset.map! {|string|
          string.strip.split(use_this_token_for_the_split_action)
        }
      end
      # ===================================================================== #
      # The first row, which will be removed, can be used as the main
      # header for our gtk-datastructure.
      # ===================================================================== #
      list_store = ::Gtk::ListStore.new(
        *([String] * dataset.first.size)
      )
      tree_view = ::Gtk::TreeView.new(list_store)
      use_these_headers = dataset.shift
      if extra_commands == :strip_quotes
        if use_these_headers.is_a? String
          use_these_headers.delete!('"')
        else
          use_these_headers.map! {|entry| entry.delete('"') }
        end
      end
      tree_view.use_these_headers = use_these_headers
      dataset.each {|inner_array|
        iter = list_store.append
        inner_array.each_with_index {|inner_entry, index|
          if extra_commands
           case extra_commands
           # ================================================================ #
           # === :strip_quotes
           # ================================================================ #
           when :strip_quotes
             inner_entry.delete!('"')
           end
          end
          iter.set_value(index, inner_entry)
        }
      }
      tree_view.headers_can_be_moved
      return tree_view
    end
  end; self.instance_eval { alias csv read_in_from_this_csv_file } # === Gtk.csv

  # ========================================================================= #
  # === Gtk.translate_this_gtk_c_code_into_ruby_code
  #
  # This method shall translate GTK code written in C into the
  # equivalent ruby code. It is incomplete, and may never work
  # perfectly well but the idea is to have this method be
  # useful as a first, initial "sanitizer".
  #
  # Perhaps one day I'll write a proper replacer. For now this is
  # all super ad-hoc.
  #
  # Usage example:
  #
  #   Gtk.translate_this_gtk_c_code_into_ruby_code ' button = gtk_button_new ();
  #   area = gtk_drawing_area_new ();
  #   g_signal_connect (area, "draw", G_CALLBACK (draw_color), (gpointer) color);
  #   gtk_widget_set_size_request (area, 24, 24);
  #   gtk_container_add (GTK_CONTAINER (button), area);
  #   gtk_widget_show_all (button);'
  #
  # ========================================================================= #
  def self.translate_this_gtk_c_code_into_ruby_code(i)
    if i.is_a? Array
      i = i.join(' ').strip
    end
    i.gsub!(
      / \(\)/,
      '()'
    ) # The ' ()' will become '()' here.
    lines = i.split("\n").map {|line|
      line = line.dup if line.frozen?
      line.strip!
      line.chop! if line.end_with? ';'
      line
    }
    i = lines.join("\n")
    return i
  end

  # ========================================================================= #
  # === Gtk.return_random_gtk_class
  #
  # This method will return a random gtk-class from my custom gtk-widgets.
  #
  # This may return a String such as:
  #
  #   "/home/Programs/Ruby/2.7.2/lib/ruby/site_ruby/2.7.0/gtk_paradise/classes/scrolled_window_with_viewport.rb"
  #
  # ========================================================================= #
  def self.return_random_gtk_class
    files = Dir[
      "#{project_base_directory?}core_classes/**/**.rb"
    ]
    files.sample
  end

  # ========================================================================= #
  # === Gtk.camelcase
  # ========================================================================= #
  def self.camelcase(i)
    i.split('_').map { |entry| entry.capitalize }.join
  end

  # ========================================================================= #
  # === Gtk.instantiate_a_random_gtk_class
  # ========================================================================= #
  def self.instantiate_a_random_gtk_class(be_verbose = true)
    this_file = return_random_gtk_class
    instance = camelcase(
      File.basename(this_file).sub(/\.rb$/,'')
    )
    if be_verbose
      e 'Trying to instantiate a new object from '
      e ::Colours.sfancy(this_file) if Object.const_defined? :Colours
    end
    require this_file
    instance = self.const_get(instance)
    return instance.new
  end

  # ========================================================================= #
  # === Gtk.set_clipboard_text
  # ========================================================================= #
  def self.set_clipboard_text(
      i = ''
    )
    clipboard?.text = i
  end

  # ========================================================================= #
  # === Gtk.clipboard_text?
  #
  # Query method to obtain the value of the clipboard text in use.
  # ========================================================================= #
  def self.clipboard_text?
    result = ''.dup
    clipboard?.request_text { |cb, text| result << text.to_s } if clipboard?
    result
  end

  # ========================================================================= #
  # === Gtk.reset_clipboard
  # ========================================================================= #
  def self.reset_clipboard
    ::Gtk.set_clipboard_text('')
  end

  # ========================================================================= #
  # === Gtk.right_mouse_click?
  # ========================================================================= #
  def self.right_mouse_click?(event)
    event.is_a?(Gdk::EventButton) and
   (event.event_type.name == 'GDK_BUTTON_PRESS') and
   (event.button == 3)
  end; self.instance_eval { alias right_mouse_button_clicked? right_mouse_click? } # === Gtk.right_mouse_button_clicked?
       self.instance_eval { alias event_right_mouse_button?   right_mouse_click? } # === Gtk.event_right_mouse_button?
       self.instance_eval { alias on_right_mouse_button_click right_mouse_click? } # === Gtk.on_right_mouse_button_click

  # ========================================================================= #
  # === Gtk.unicode_horizontal_spacer
  # ========================================================================= #
  def self.unicode_horizontal_spacer
    '―'
  end

  # ========================================================================= #
  # === Gtk.options
  #
  # This is a bit similar to HTML dropdown menu with its own options.
  # ========================================================================= #
  def self.options(*i)
    array_entries = []
    vbox = ::Gtk.vbox
    main_button = nil
    i.flatten.each_with_index {|entry, index|
      case index
      when 0 # This is the main button. The others will point to it.
        button = ::Gtk::RadioButton.new(label: entry)
        main_button = button
      else
        button = ::Gtk::RadioButton.new(
          label: entry,
          member: main_button # Associate it with the main button here.
        )
      end
      array_entries << button
      vbox.minimal(button)
    }
    return [vbox, array_entries] # Return an Array.
  end

  # ========================================================================= #
  # === Gtk.return_path_to_the_sitelibdir
  #
  # This method may return a path, as a String, such as
  # "/usr/lib/ruby/site_ruby/2.7.0/" or
  # "/home/Programs/Ruby/3.0.2/lib/ruby/site_ruby/3.0.0/".
  # ========================================================================= #
  def self.return_path_to_the_sitelibdir
    "#{RbConfig::CONFIG['sitelibdir'].dup}/"
  end; self.instance_eval { alias ruby_sitedir? return_path_to_the_sitelibdir } # === Gtk.ruby_sitedir?

  # ========================================================================= #
  # === Gtk.gtk_hseparator
  #
  # This is a horizontal separator.
  # ========================================================================= #
  def self.gtk_hseparator
    ::Gtk::Separator.new(:horizontal)
  end; self.instance_eval { alias gtk_separator        gtk_hseparator } # === Gtk.gtk_separator
       self.instance_eval { alias horizontal_separator gtk_hseparator } # === Gtk.horizontal_separator
       self.instance_eval { alias hsep                 gtk_hseparator } # === Gtk.hsep

  # ========================================================================= #
  # === Gtk.coloured_button
  #
  # The first argument to this method should be a String.
  #
  # A typical usage example goes like this:
  #
  #   coloured_button('_DBGET_ivory', false)
  #
  # ========================================================================= #
  def self.coloured_button(
      i                        = '',
      optional_second_argument = false
    )
    i = i.dup if i.frozen?
    unless i.empty?
      if i.count('_') < 2
        # =================================================================== #
        # In this case we must append the default colour, via a leading
        # '_' token.
        # =================================================================== #
        i << "_#{Gtk::ColouredButton.default_colour?}"
      end
    end
    begin
      require 'gtk_paradise/widgets/gtk3/coloured_button/coloured_button.rb'
    rescue LoadError => error; pp error; end
    coloured_button = ::Gtk::ColouredButton.new(i, optional_second_argument)
    return coloured_button
  end

  # ========================================================================= #
  # === Gtk.set_focus_and_select
  #
  # A Gtk::Entry should be passed to this method.
  # ========================================================================= #
  def self.set_focus_and_select(this_entry)  
    this_entry.set_focus(true) 
    this_entry.select_region(0, -1)
  end

  # ========================================================================= #
  # === Gtk.clipboard_get
  #
  # Obtain an instance/handle to the Clipboard functionality made available
  # through Gtk.
  # ========================================================================= #
  def self.clipboard_get
    @clipboard = ::Gtk::Clipboard.get(
      Gdk::Atom.intern('CLIPBOARD',true)
    )
    return @clipboard
  end; self.instance_eval { alias clipboard_handle clipboard_get } # === Gtk.clipboard_Handle
       self.instance_eval { alias create_clipboard clipboard_get } # === Gtk.create_clipboard
       self.instance_eval { alias clipboard?       clipboard_get } # === Gtk.clipboard?

  # ========================================================================= #
  # === Gtk.return_black_pixbuf
  #
  # This method will merely make a black "dot", a pixbuf. In the future we
  # may have to adjust this, e. g. to generate any coloured dot as-is.
  # ========================================================================= #
  def self.return_black_pixbuf(
      use_this_width  = 10,
      use_this_height = 10,
      use_this_as_the_fill_colour = 0x00000000
    )
    black_pixbuf = GdkPixbuf::Pixbuf.new(
      colorspace:      :rgb,
      has_alpha:       false,
      bits_per_sample: 8,
      width:           use_this_width,
      height:          use_this_height
    )
    # use_this_as_the_fill_colour = Gdk::Color.parse('steelblue') # black'
    black_pixbuf.fill!(use_this_as_the_fill_colour)
    return black_pixbuf
  end

  # ========================================================================= #
  # === Gtk.do_exit
  # ========================================================================= #
  def self.do_exit
    Gtk.main_quit
  end

  # ========================================================================= #
  # === Gtk.pkg_config?
  #
  # This method will yield the pkg-config related flags, as a String.
  # ========================================================================= #
  def self.pkg_config?(
      which_version = :gtk3
    )
    case which_version
    # ======================================================================= #
    # === :gtk3
    # ======================================================================= #
    when :gtk3
      which_version = 'gtk+-3.0'
    end
    return `pkg-config --cflags --libs #{which_version}`
  end

  # ========================================================================= #
  # === Gtk.standard_file_chooser_dialog
  #
  # This method will return a file chooser dialog.
  #
  # It requires two arguments:
  #
  #   (1) the title for the FileChooserDialog action.
  #   (2) the widget which handles this.
  #
  # ========================================================================= #
  def self.standard_file_chooser_dialog(
      title = 'Choose a file',
      widget
    )
    open_symbol = :open
    cancel_symbol = :cancel
    response_accept = :accept
    if use_gtk2?
      cancel_symbol   = ::Gtk::Dialog::RESPONSE_CANCEL
      response_accept = ::Gtk::Dialog::RESPONSE_ACCEPT
      open_symbol     = ::Gtk::FileChooser::ACTION_OPEN
    end
    _ = ::Gtk::FileChooserDialog.new(
      title: title,
      parent: widget,
      action: open_symbol,
      buttons: [[::Gtk::Stock::CANCEL, cancel_symbol], 
                [::Gtk::Stock::OPEN,   response_accept]]
    )
    return _
  end

  # ========================================================================= #
  # === Gtk.set_tooltip
  #
  # The first argument to this method is the text that is to be displayed,
  # as a String. For example, "Hello world!".
  #
  # The second argument shall be the widget that receives the tooltip.
  # For example, a gtk-button as widget, or a gtk-label.
  #
  # Note that Gtk::Tooltips has been deprecated in ruby-gtk3, so this is
  # only useful for ruby-gtk2. However had, even in ruby-gtk2 it is not
  # recommended to use it, as there are other, simpler means to set
  # a tooltip - namely via .tooltip_text=.
  #
  # Usage example:
  #
  #   Gtk.set_tooltip('Hello world!', widget)
  #
  # ========================================================================= #
  def self.set_tooltip(
      which_tip_to_use          = 'This is a popup.',
      which_widget_to_use_it_on = nil
    )
    tooltip = ::Gtk::Tooltip.new
    tooltip.set_text(which_tip_to_use)
    return tooltip
  end; self.instance_eval { alias tooltip                     set_tooltip } # === Gtk.tooltip
       self.instance_eval { alias tooltip_widget              set_tooltip } # === Gtk.tooltip_widget
       self.instance_eval { alias set_tooltip_for_this_widget set_tooltip } # === Gtk.set_tooltip_for_this_widget

  # ========================================================================= #
  # === Gtk.tooltip_for
  #
  # This is like the above method (Gtk.set_tooltip), except that the 
  # arguments are reversed.
  #
  # First comes the target-widget, then the specific message to display.
  #
  # Two aliass exist for this method, called Gtk.set_tooltip_for() and
  # Gtk.fancy_tooltip=() respectively.
  #
  # The reason as to why this method was added was because I sometimes
  # confused the order of arguments, so I found it simpler in these
  # cases to use another API instead.
  # ========================================================================= #
  def self.tooltip_for(widget, text)
    ::Gtk.set_tooltip(text, widget)
  end; self.instance_eval { alias set_tooltip_for tooltip_for } # === Gtk.set_tooltip_for
       self.instance_eval { alias fancy_tooltip=  tooltip_for } # === Gtk.fancy_tooltip=

  # ========================================================================= #
  # === Gtk.do_select_file
  #
  # This method can be used to select a specific (local) file.
  #
  # The filename will be returned by this method, as a String.
  #
  # A complex usage example follows next:
  #
  #   filename = ::Gtk.select_file(parent_widget_here) {{
  #     current_folder:             current_folder,
  #     show_hidden:                true,
  #     extra_widget:               @extra_button,
  #     add_these_shortcut_folders: ARRAY_ADD_THESE_SHORTCUT_FOLDERS,
  #     use_this_file_filter:       '.pdf'
  #   }}
  #
  # ========================================================================= #
  def self.do_select_file(
      parent_widget,
      use_this_file_filter = nil
    )
    yielded = nil
    # ======================================================================= #
    # === Handle blocks given to this method
    # ======================================================================= #
    if block_given?
      yielded = yield
    end
    action_option = :open
    # ======================================================================= #
    # Next, assume ruby-gtk3:
    # ======================================================================= #
    if use_gtk3? # else handle ruby-gtk3. ruby-gtk3 is not yet supported here.
      dialog = Gtk::FileChooserDialog.new(
        title: 'Open File',
        action: action_option,
        buttons: [[Gtk::Stock::CANCEL, :cancel],
                  [Gtk::Stock::OPEN,   :accept]]
      )
    end
    # ======================================================================= #
    # === Handle Hashes given to this method next
    # ======================================================================= #
    if yielded and yielded.is_a?(Hash)
      # ===================================================================== #
      # === :current_folder
      #
      # Usage example for this entry point:
      #
      #   ::Gtk.select_file {{ current_folder: '/home/' }}
      #
      # ===================================================================== #
      if yielded.has_key? :current_folder
        _ = yielded[:current_folder]
        dialog.set_current_folder(_)
      end
      # ===================================================================== #
      # === :show_hidden
      # ===================================================================== #
      if yielded.has_key? :show_hidden
        dialog.show_hidden = yielded[:show_hidden]
      end
      # ===================================================================== #
      # === :extra_widget
      # ===================================================================== #
      if yielded.has_key? :extra_widget
        dialog.extra_widget = yielded[:extra_widget]
      end
      # ===================================================================== #
      # === :add_these_shortcut_folders
      # ===================================================================== #
      if yielded.has_key? :add_these_shortcut_folders
        _ = [yielded[:add_these_shortcut_folders]].flatten.compact
        _.each {|this_directory|
          dialog.add_shortcut_folder(this_directory)
        }
      # ===================================================================== #
      # === :additional_directories
      # ===================================================================== #
      elsif yielded.has_key? :additional_directories
        _ = [yielded[:additional_directories]].flatten.compact
        _.each {|this_directory|
          dialog.add_shortcut_folder(this_directory)
        }
      end
      # ===================================================================== #
      # === :use_this_file_filter
      # ===================================================================== #
      if yielded.has_key? :use_this_file_filter
        use_this_file_filter = yielded[:use_this_file_filter]
      # ===================================================================== #
      # === :filter_for_these_file_types
      # ===================================================================== #
      elsif yielded.has_key? :filter_for_these_file_types
        use_this_file_filter = yielded[:filter_for_these_file_types]
      end
    end
    if use_this_file_filter
       use_this_file_filter = use_this_file_filter.to_s.dup
       if use_this_file_filter.is_a? String
         filter = ::Gtk::FileFilter.new
         filter.name = use_this_file_filter.to_s+' files'
         unless use_this_file_filter.start_with? '.'
           use_this_file_filter.prepend('.')
         end
         unless use_this_file_filter.start_with? '*'
           use_this_file_filter.prepend('*')
         end
         use_this_file_filter = filter.add_pattern(use_this_file_filter)
       end
       dialog.add_filter(use_this_file_filter)
    end
    # ======================================================================= #
    # === Add some default shortcut-folders on Roebe
    #
    # This is only useful for my home setup really.
    # ======================================================================= #
    if is_on_roebe?
      begin
        dialog.add_shortcut_folder ENV['MY_SONGS']
      rescue Exception; end
      begin
        dialog.add_shortcut_folder ENV['MY_TEMP']
      rescue Exception; end
      begin
        dialog.add_shortcut_folder ENV['MY_DATA']
      rescue Exception; end
      begin
        dialog.add_shortcut_folder ENV['RUBY_SRC']
      rescue Exception; end
      # ===================================================================== #
      # Add some more directories by default next:
      # ===================================================================== #
      dialog.add_shortcut_folder '/Depot/j/'    unless Dir.pwd.include?('/Depot/j')
      dialog.add_shortcut_folder '/Depot/jj/'   unless Dir.pwd.include?('/Depot/jj')
      dialog.add_shortcut_folder '/Depot/jjj/'  unless Dir.pwd.include?('/Depot/jjj')
      dialog.add_shortcut_folder '/Depot/jjjj/' unless Dir.pwd.include?('/Depot/jjjj')
      # dialog.add_shortcut_folder '/' # <- Can not use this variant.
    end
    accept = :accept
    case dialog.run
    when accept
      puts "filename = #{dialog.filename}"
    end
    result = dialog.filename
    dialog.destroy # Clean it up here.
    return result
  end; self.instance_eval { alias select_file do_select_file } # === Gtk.select_file

  # ========================================================================= #
  # === Gtk.vertical_spacer
  # ========================================================================= #
  def self.vertical_spacer
    # Assume gtk3 and gtk4 here.
    ::Gtk::Separator.new(:vertical)
  end; self.instance_eval { alias vspacer vertical_spacer } # === Gtk.vspacer

  # ========================================================================= #
  # === Gtk.initialize_gtk3
  # ========================================================================= #
  def self.initialize_gtk3
    require 'gtk3'
  end

  # ========================================================================= #
  # === Gtk.initialize_gtk4
  # ========================================================================= #
  def self.initialize_gtk4
    require 'gtk4'
  end

  # ========================================================================= #
  # === Gtk.replace_all_URL_entries_in_this_string
  #
  # This method was added mostly for the gem called "studium", so that
  # URL entries can be replaced with the corresponding <a href> tags.
  #
  # Other projects that may want to have clickable URL tags in ruby-gtk
  # could make use of this method as well, so it was added to the
  # gtk_paradise gem in January 2021.
  #
  # See this entry at rubular.com:
  #
  #   https://rubular.com/r/x8acuV7BilRH12
  #
  # Usage example:
  #
  #   x = Gtk.replace_all_URL_entries_in_this_string('- Foobar. URL: https://de.wikipedia.org/wiki/Amygdala')
  #
  # ========================================================================= #
  def self.replace_all_URL_entries_in_this_string(
      i = '- Foobar. URL: https://de.wikipedia.org/wiki/Amygdala'
    )
    use_this_regex = /URL: (.+)\s?/
    i.gsub(use_this_regex, '<a href="\1">\1</a>') # Make the href elements clickable.
  end

  # ========================================================================= #
  # === Gtk.enter_key?
  #
  # This method will return true or false depending on whether the given
  # input-event (a gdk-event) is the enter key. Two keys qualify for
  # this right now: Return, and KP_Enter.
  #
  # You have to pass a specific gdk-event into this method, which should
  # an instance of Gdk::EventKey.
  #
  # For instance:
  #
  #   .signal_connect(:clicked) {|widget, event|
  #
  # would qualify normally.
  # ========================================================================= #
  def self.enter_key?(event)
    case Gdk::Keyval.to_name(event.keyval)
    when 'Return','KP_Enter'
      true
    else
      false
    end
  end; self.instance_eval { alias is_enter_key? enter_key? } # === Gtk.is_enter_key?

  # ========================================================================= #
  # === Gtk.table
  #
  # This method will return a Gtk::Table object.
  # ========================================================================= #
  def self.table
    return ::Gtk::Table.new
  end

  # ========================================================================= #
  # === Gtk.create_table
  # ========================================================================= #
  def self.create_table(a = 20, b = 10, c = true)
    return ::Gtk::Table.new(a, b, c)
  end

  # ========================================================================= #
  # === Gtk.table1
  #
  # This method will return a Gtk table with a 1 columns layout.
  # ========================================================================= #
  def self.table1(*all_args)
    table = ::Gtk::Table.new(all_args.size, 2, true)
    counter = 0
    slow_counter = 0
    all_args.each {|element|
      table.attach_defaults( element,
        (counter % 1), (counter % 1)+1, slow_counter, slow_counter+1
      )
      counter += 1
      slow_counter += 1 # if  counter % 2 == 0
    }
    return table
  end

end

if __FILE__ == $PROGRAM_NAME
  alias e puts
  e Gtk.is_on_roebe?
  e
  pp Gtk.read_in_from_this_csv_file(ARGV)
  e
  e Gtk.old_verbose_value?
  e
  # require 'gtk2'
  # pp Gtk.is_gtk2_in_use?
    alias e puts
  e
  e Gtk.return_path_to_the_sitelibdir
  e
  e Gtk.pkg_config?
  e
  # ========================================================================= #
  # The next code specifically tests the Gtk.return_black_pixbuf() method.
  # ========================================================================= #
  require 'gtk3'
  window = Gtk::Window.new
  box = Gtk::Box.new(:horizontal)
  box.add(
    Gtk::Image.new(pixbuf: Gtk.return_black_pixbuf)
  )
  window.add(box)
  window.move(0, 0)
  window.set_size_request(50, 50)
  window.show_all
  Gtk.main
end