cedlemo/topinambour

View on GitHub
lib/color_selector.rb

Summary

Maintainability
A
1 hr
Test Coverage
# Copyright 2015-2018 Cedric LE MOIGNE, cedlemo@gmx.com
# This file is part of Topinambour.
#
# Topinambour is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# any later version.
#
# Topinambour is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Topinambour.  If not, see <http://www.gnu.org/licenses/>.

TERMINAL_COLOR_NAMES = [:black, :red, :green, :yellow,
                        :blue, :magenta, :cyan, :white]

class TopinambourColorSelector < Gtk::Grid
  attr_reader :colors
  def initialize(window)
    @window = window
    super()

    reset_button = generate_reset_button
    attach(reset_button, 0, 0, 1, 1)

    initialize_default_colors
    add_color_selectors

    save_button = generate_save_button
    attach(save_button, 0, 1, 1, 1)

    import_button = generate_import_button
    attach(import_button, 10, 0, 1, 1)

    export_button = generate_export_button
    attach(export_button, 10, 1, 1, 1)

    show_all
    set_halign(:center)
    set_valign(:end)
    set_name("topinambour-color-selector")
  end

  private

  def generate_import_export_button(name, save_in=nil)
    button = ColorSchemeSelector.new(name, @window, save_in)
    button.signal_connect "clicked" do |widget|
      filename = widget.run_chooser_dialog
      if filename then
        yield filename if block_given?
      end
      widget.chooser_destroy
    end
    button
  end

  def generate_import_button
    generate_import_export_button("Import") do |filename|
      file = File.read(filename)

      foreground = file[/\*\.?foreground:\s*(.*)/, 1]
      child = get_child_at(1, 0)
      child.rgba = Gdk::RGBA.parse(foreground) unless foreground.nil?
      @colors[0] = child.rgba

      background = file[/\*\.?background:\s*(.*)/, 1]
      child = get_child_at(1, 1)
      child.rgba =  Gdk::RGBA.parse(background) unless background.nil?
      @colors[1] = child.rgba
      #
      # Normal colors top row
      TERMINAL_COLOR_NAMES.each_with_index do |_, i|
        color = file[/\*\.?color#{i}:\s*(.*)/, 1]
        child = get_child_at(2 + i, 0)
        child.rgba = Gdk::RGBA.parse(color) unless color.nil?
        @colors[2 + i] = child.rgba
      end

      # Bright colors bottom row
      TERMINAL_COLOR_NAMES.each_with_index do |_, i|
        color = file[/\*\.?color#{i + 8}:\s*(.*)/, 1]
        child = get_child_at(2 + i, 1)
        child.rgba = Gdk::RGBA.parse(color) unless color.nil?
        @colors[10 + i] = child.rgba
      end
      apply_new_colors
    end
  end

  def double_to_hex(d)
    (d * 255).to_i.to_s(16)
  end

  def rgba_to_hex_string(rgba)
    "##{double_to_hex(rgba.red)}#{double_to_hex(rgba.green)}#{double_to_hex(rgba.blue)}"
  end

  def generate_export_button
    generate_import_export_button("Export", "untitled") do |filename|
      File.open(filename, 'a') do |file|
        child = get_child_at(1, 0)
        file.puts "*.foreground: #{rgba_to_hex_string(child.rgba)}"

        child = get_child_at(1, 1)
        file.puts "*.background: #{rgba_to_hex_string(child.rgba)}"

        # Normal colors top row
        TERMINAL_COLOR_NAMES.each_with_index do |_, i|
          child = get_child_at(2 + i, 0)
          file.puts "*.color#{i}: #{rgba_to_hex_string(child.rgba)}"
        end

        # Bright colors bottom row
        TERMINAL_COLOR_NAMES.each_with_index do |_, i|
          child = get_child_at(2 + i, 1)
          file.puts "*.color#{i + 8}: #{rgba_to_hex_string(child.rgba)}"
        end
      end
    end
  end

  def initialize_default_colors
    colors_strings = @window.application.settings["colorscheme"]
    @default_colors = colors_strings.map {|c| Gdk::RGBA.parse(c) }
    @colors = @default_colors.dup
  end

  def generate_reset_button
    button = Gtk::Button.new(:label => "Reset")
    button.signal_connect "clicked" do
      initialize_default_colors
      # foreground
      get_child_at(1, 0).rgba = @default_colors[0]
      # background
      get_child_at(1, 1).rgba = @default_colors[1]

      # Normal colors top row
      TERMINAL_COLOR_NAMES.each_with_index do |_, i|
        get_child_at(2 + i, 0).rgba = @default_colors[2 + i]
      end

      # Bright colors bottom row
      TERMINAL_COLOR_NAMES.each_with_index do |_, i|
        get_child_at(2 + i, 1).rgba = @default_colors[10 + i]
      end
      apply_new_colors
    end
    button
  end

  def apply_new_properties
    colors_strings = @colors.map { |c| c.to_s }
    @window.application.settings["colorscheme"] = colors_strings
    @window.terminal.load_colors
    @window.terminal.load_settings
  end

  def generate_save_button
    button = Gtk::Button.new(:label => "Save")
    button.signal_connect "clicked" do |widget|
      apply_new_properties
      @window.exit_overlay_mode
    end
    button
  end

  def add_color_selector(name, i, position=nil)
    color_sel = Gtk::ColorButton.new(@default_colors[i])
    color_sel.title = name.to_s
    color_sel.name = "topinambour-button-#{name}"
    color_sel.tooltip_text = name.to_s
    color_sel.signal_connect "color-set" do
      @colors[i] = color_sel.rgba
      apply_new_colors
    end
    attach(color_sel, position[0], position[1], 1, 1)
  end

  def add_color_selectors
    add_color_selector("foreground", 0, [1, 0])
    add_color_selector("background", 1, [1, 1])
    TERMINAL_COLOR_NAMES.each_with_index do |name, i|
      add_color_selector(name, i + 2, [2 + i, 0])
    end

    TERMINAL_COLOR_NAMES.each_with_index do |name, i|
      add_color_selector("bright#{name}", i + 10, [2 + i, 1])
    end
  end

  def apply_new_colors
    @window.terminal.colors = @colors
  end
end

class ColorSchemeSelector < Gtk::Button
  def initialize(label, parent, save_in)
    @parent = parent
    @save_in = save_in
    @action = @save_in.nil? ? :open : :save
    super(:label => label)
  end

  def run_chooser_dialog
    @dialog = Gtk::FileChooserDialog.new(:title => label,
                                         :parent => @parent,
                                         :action => @action,
                                         :buttons => [[label, :ok],
                                                      ["Cancel", :cancel]])
    @dialog.current_name = @save_in unless @save_in.nil?
    if @dialog.run == :ok then
      @dialog.filename
    end
  end

  def chooser_destroy
    @dialog.destroy
  end
end