#!/usr/bin/ruby -w
# Encoding: UTF-8
# frozen_string_literal: true
# =========================================================================== #
# Gtk::Print* sample in Ruby, by Mathieu Blondel.
#
# Published under the same license as Ruby-GNOME2.
# =========================================================================== #
require 'gtk2'

# =========================================================================== #
# Values returned by a Pango::Layout or set in a Pango::Layout
# are always multiple of Pango::SCALE. Those are convenience
# methods to deal with that.
# =========================================================================== #

class Pango::Layout
  def size_in_points
    self.size.collect { |v| v / Pango::SCALE }
  end
  def width_in_points
    self.size[0] / Pango::SCALE
  end
  def height_in_points
    self.size[1] / Pango::SCALE
  end
  def width_in_points=(width)
    self.width = width * Pango::SCALE
  end
end

class Print < Gtk::PrintOperation

  FONT = Pango::FontDescription.new('sans 12')
  FONT_SMALL = Pango::FontDescription.new('sans 8')

  FONT_SIZE = 12
  FONT_SMALL_SIZE = 8

  # ========================================================================= #
  # === initialize
  # ========================================================================= #
  def initialize(parent_window, text)
    super()
    @parent_window = parent_window
    @text = text
    # with this option disabled, the origin is the the upper left corner
    # *taking into consideration margins* !
    self.use_full_page = false
    self.unit = Gtk::PaperSize::UNIT_POINTS
    # set default paper size
    page_setup = Gtk::PageSetup.new
    paper_size = Gtk::PaperSize.new(Gtk::PaperSize.default)
    page_setup.paper_size_and_default_margins = paper_size
    self.default_page_setup = page_setup
    # show a progress bar
    self.show_progress = true
    # You must choose between "paginate" and "begin-print".
    #
    # "begin-print" is emitted only once and "paginate" is emitted
    # until the block returns true.
    # In both cases, you must set the number of pages at the end with
    # Gtk::PrintOperation#n_pages=
    signal_connect(:begin_print) { |pop, context|
      puts "calls begin-print"
      cr = context.cairo_context
      paragraphs = @text.split("\n")
      layouts = []
      paragraphs.each { |para|
        layouts << create_layout(cr, para)
      }

      # ===================================================================== #
      # distribute paragraph layouts among pages
      # ===================================================================== #
      @page_layouts = []

      curr_height = 0
      n_pages = 0
      layouts.each { |layout|
        height = layout.height_in_points
        if curr_height + height > real_page_height
          n_pages += 1
          curr_height = 0
        end
        @page_layouts[n_pages] ||= []
        @page_layouts[n_pages] << layout
        curr_height += height
      }

      pop.n_pages = n_pages + 1
    }

    signal_connect(:draw_page) { |pop, context, page_num|
      puts "calls draw-page %d" % page_num
      cr = context.cairo_context
      x, y = [0,0] # this means we start at the upper left margin  

      if @page_layouts[page_num]
        @page_layouts[page_num].each { |layout|
          cr.move_to(x,y)
          cr.show_pango_layout(layout)      
          y += layout.height_in_points
        }
        total_pages = @page_layouts.length
      else
        total_pages = 1
      end

      # page_num starts at 0
      draw_footer(cr, page_num + 1, total_pages)
    }
  end

  # ========================================================================= #
  # === run_print_dialog
  # ========================================================================= #
  def run_print_dialog
    res = run(ACTION_PRINT_DIALOG, @parent_window)
    case res
    when RESULT_ERROR
      puts 'error'
    when RESULT_CANCEL
      puts 'cancelled'
    when RESULT_APPLY
      puts 'applied'
    when RESULT_IN_PROGRESS
      puts 'in progress'
    end
  end

  # ========================================================================= #
  # === run_preview
  # ========================================================================= #
  def run_preview
    run(ACTION_PREVIEW, @parent_window)
  end

  private
  # ========================================================================= #
  # === page_height
  # ========================================================================= #
  def page_height
    setup = self.default_page_setup
    # this takes margins into consideration, contrary to get_paper_height
    setup.get_page_height(Gtk::PaperSize::UNIT_POINTS)
  end

  # ========================================================================= #
  # === page_width
  # ========================================================================= #
  def page_width
    setup = self.default_page_setup
    setup.get_page_width(Gtk::PaperSize::UNIT_POINTS)
  end

  # ========================================================================= #
  # === real_page_height
  # ========================================================================= #
  def real_page_height
    page_height - footer_height
  end

  # ========================================================================= #
  # === footer_height
  # ========================================================================= #
  def footer_height
    5 * FONT_SMALL_SIZE
  end

  # ========================================================================= #
  # === create_layout
  # ========================================================================= #
  def create_layout(cr, text)
    layout = cr.create_pango_layout
    layout.width_in_points = page_width
    layout.font_description = FONT
    layout.wrap = Pango::Layout::WRAP_CHAR
    layout.ellipsize = Pango::Layout::ELLIPSIZE_NONE
    layout.single_paragraph_mode = false
    layout.text = text
    layout
  end

  # ========================================================================= #
  # === draw_footer
  # ========================================================================= #
  def draw_footer(cr, nth_page, total_page)    
    layout = cr.create_pango_layout
    layout.alignment = :right
    layout.font_description = FONT_SMALL
    layout.text = "Page %d/%d" % [nth_page, total_page] + "\n" + \
      Time.now.strftime("Printed on %Y/%m/%d at %H:%M") + "\n" + \
        'Powered by Ruby-GNOME2!'
    width, height = layout.size_in_points
    x, y = [page_width, page_height]
    x -= width
    y -= height
    cr.move_to(x, y)
    cr.show_pango_layout(layout)
    cr.rel_move_to(width, -2)
    cr.rel_line_to(-page_width, 0)
    cr.stroke
  end

end

class Window < Gtk::Window

  # ========================================================================= #
  # === initialize
  # ========================================================================= #
  def initialize
    super()
    signal_connect(:delete_event) { Gtk.main_quit }
    set_default_size(600, 600)
    @textview = Gtk::TextView.new
    @textview.wrap_mode = Gtk::TextTag::WRAP_WORD
    hbox = Gtk::HBox.new
    page_setup_button = Gtk::Button.new
    page_setup_button.label = 'Page setup'
    page_setup_button.signal_connect(:clicked) {
      # When the user clicks on the page-setup button, a setup
      # Dialog is created, as a popup-widget.
      @page_setup = Print.run_page_setup_dialog(self, # parent window
                      @page_setup)
    }
    # The print-preview button follows next.
    print_preview_button = Gtk::Button.new(Gtk::Stock::PRINT_PREVIEW)
    print_preview_button.signal_connect(:clicked) {
      printop = Print.new(self, @textview.buffer.text)
      printop.default_page_setup = @page_setup if @page_setup
      printop.run_preview
    }

    print_button = Gtk::Button.new(Gtk::Stock::PRINT)
    print_button.signal_connect(:clicked) {
      printop = Print.new(self, @textview.buffer.text)
      printop.default_page_setup = @page_setup if @page_setup
      printop.run_print_dialog
    }

    [ page_setup_button, print_preview_button, print_button].each { |b|
      hbox.pack_start(b, true, true) # expand, fill
    }
    vbox = Gtk::VBox.new
    scroll = Gtk::ScrolledWindow.new.add(@textview)    
    scroll.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC)
    vbox.pack_start(scroll)
    vbox.pack_end(hbox, false, false)
    add(vbox)
    show_all
  end

end

window = Window.new
window.move(0, 0)
Gtk.main
