yast/yast-journal

View on GitHub
src/lib/y2journal/query_dialog.rb

Summary

Maintainability
A
45 mins
Test Coverage
# Copyright (c) 2014 SUSE LLC.
#  All Rights Reserved.

#  This program is free software; you can redistribute it and/or
#  modify it under the terms of version 2 or 3 of the GNU General
#  Public License as published by the Free Software Foundation.

#  This program 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 this program; if not, contact SUSE LLC.

#  To contact Novell about this file by physical or electronic mail,
#  you may find current contact information at www.suse.com

require "yast"
require "ui/dialog"
require "y2journal/time_helpers"
require "y2journal/query_presenter"

Yast.import "UI"
Yast.import "Label"

module Y2Journal
  # Dialog allowing the user to set the query used to display the journal
  # entries in Y2Journal::EntriesDialog
  #
  # It returns a QueryPresenter object.
  #
  # @see Y2Journal::EntriesDialog
  class QueryDialog < UI::Dialog
    include TimeHelpers

    # Width of the input widgets for the filters
    INPUT_WIDTH = 20

    def initialize(query)
      super()
      textdomain "journal"
      @query = query
    end

    # Main layout
    def dialog_content
      VBox(
        # Header
        Heading(_("Entries to display")),
        # Interval
        Frame(
          _("Time interval"),
          interval_widget
        ),
        VSpacing(0.3),
        # Filters
        Frame(
          _("Filters"),
          filters_widget
        ),
        VSpacing(0.3),
        # Footer buttons
        HBox(
          PushButton(Id(:cancel), Yast::Label.CancelButton),
          PushButton(Id(:ok), Yast::Label.OKButton)
        )
      )
    end

    # Event callback for the 'ok' button
    def ok_handler
      finish_dialog(query_from_widgets)
    end

    # Registers the callbacks for changes in the UI
    def self.define_event_callbacks
      # Event callbacks for changes in the interval time widgets
      [:until_date, :until_time, :since_date, :since_time].each do |widget|
        define_method(:"#{widget}_handler") do
          # Automatically select the dates-based interval
          Yast::UI.ChangeWidget(Id(:interval), :CurrentButton, "Hash")
        end
      end

      # Event callbacks for changes in the filter fields
      QueryPresenter.filters.each do |filter|
        define_method(:"#{filter[:name]}_value_handler") do
          # Automatically check the corresponding checkbox
          Yast::UI.ChangeWidget(Id(filter[:name]), :Value, true)
        end
      end
    end

    define_event_callbacks

  private

    # Translates the value of the widgets to a new QueryPresenter object
    def query_from_widgets
      interval = Yast::UI.QueryWidget(Id(:interval), :CurrentButton)
      if interval == "Hash"
        interval = {
          since: time_from_widgets_for(:since),
          until: time_from_widgets_for(:until)
        }
      end

      filters = {}
      QueryPresenter.filters.each do |filter|
        name = filter[:name]
        # Skip if the checkbox is not checked
        next unless Yast::UI.QueryWidget(Id(name), :Value)
        # Read the widget...
        value = widget_to_filter(name, filter[:multiple])
        # ...discarding empty values
        filters[name] = value unless value.empty?
      end

      QueryPresenter.new(Query.new(interval: interval, filters: filters))
    end

    def interval_widget
      RadioButtonGroup(Id(:interval), VBox(*interval_buttons))
    end

    # Array of radio buttons to select the interval
    def interval_buttons
      QueryPresenter.intervals.map do |int|
        Left(
          HBox(
            interval_button(int),
            *interval_additional_widgets(int)
          )
        )
      end
    end

    # Radio button to select a given interval
    #
    # @param int [Hash] interval as returned by QueryPresenter.intervals
    def interval_button(int)
      selected = if int[:value] == Hash
        @query.interval.is_a?(Hash)
      else
        int[:value] == @query.interval
      end
      RadioButton(Id(int[:value].to_s), int[:label], selected)
    end

    # Additional widgets to display after the radio button for a given interval
    #
    # @param int [Hash] interval as returned by QueryPresenter.intervals
    def interval_additional_widgets(int)
      if int[:value] == Hash
        [HSpacing(1)] + dates_widgets
      else
        []
      end
    end

    # Array of widgets for selecting date/time thresholds
    def dates_widgets
      [
        *datetime_widgets_for(:since, since_value),
        Label("-"),
        *datetime_widgets_for(:until, until_value)
      ]
    end

    # Initial value for the :since widget
    def since_value
      if @query.interval.is_a?(Hash)
        @query.interval[:since]
      else
        QueryPresenter.default_since
      end
    end

    # Initial value for the :until widget
    def until_value
      if @query.interval.is_a?(Hash)
        @query.interval[:until]
      else
        QueryPresenter.default_until
      end
    end

    # Widget allowing to set the filters
    def filters_widget
      filters = QueryPresenter.filters.map do |filter|
        name = filter[:name]
        Left(
          HBox(
            CheckBox(Id(name), filter[:form_label], !@query.filters[name].nil?),
            HSpacing(1),
            widget_for_filter(name, filter[:values])
          )
        )
      end
      VBox(*filters)
    end

    # Widget to set the value of a given filter.
    #
    # If the second argument is nil, an input field will be used. Otherwise, a
    # combo box will be returned.
    #
    # @param name [Symbol] name of the filter
    # @param values [Array] optional list of values for the combo box
    def widget_for_filter(name, values = nil)
      id = Id(:"#{name}_value")
      if values
        items = values.map do |value|
          Item(Id(value), value, @query.filters[name] == value)
        end
        ComboBox(id, Opt(:notify), "", items)
      else
        MinWidth(
          INPUT_WIDTH,
          InputField(id, Opt(:notify), "", filter_to_string(name))
        )
      end
    end

    # String representing the value of a filter.
    #
    # Used to fill the corresponding input field. If the filter has multiple
    # values, they will be concatenated with a whitespace as separator.
    def filter_to_string(name)
      value = @query.filters[name]
      if value.nil?
        ""
      elsif value.is_a?(Array)
        value.join(" ")
      else
        value
      end
    end

    # Reads the widget associated to a filter and returns its value, as
    # a String or an Array of strings.
    #
    # @param name [Symbol] name of the filter
    # @param multiple [Boolean] if true, an array will be returned
    def widget_to_filter(name, multiple)
      value = Yast::UI.QueryWidget(Id(:"#{name}_value"), :Value)
      if multiple
        value.split(" ")
      else
        value
      end
    end
  end
end