/* Copyright (C) 2006 The gtkmm Development Team
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, see <https://www.gnu.org/licenses/>.
 */

#include <glibmm/object.h>

#include <gtkmm/pagesetup.h>
#include <gtkmm/printcontext.h>
#include <gtkmm/printoperationpreview.h>
#include <gtkmm/printsettings.h>
#include <gtkmm/window.h>

_DEFS(gtkmm,gtk)
_PINCLUDE(glibmm/private/object_p.h)
_PINCLUDE(gtk/gtk.h)

namespace Gtk
{

_WRAP_ENUM(PrintStatus, GtkPrintStatus, decl_prefix GTKMM_API)
_WRAP_GERROR(PrintError, GtkPrintError, GTK_PRINT_ERROR, decl_prefix GTKMM_API, decl_prefix GTKMM_API)

/** @defgroup Printing Printing
 */

/** PrintOperation is the high-level, portable printing API. It looks a bit different than other
 * GTK+ dialogs such as the FileChooser, since some platforms don't expose enough infrastructure
 * to implement a good print dialog. On such platforms, PrintOperation uses the native print
 * dialog. On platforms which do not provide a native print dialog, GTK+ uses its own, see PrintUnixDialog.
 *
 * The typical way to use the high-level printing API is to create a PrintOperation object
 * when the user chooses to print. Then you set some properties on it,such as the page size, any PrintSettings
 * from previous print operations, the number of  pages, the current page, etc.
 *
 * Then you start the print operation by calling run(). It will then show a dialog to
 * let the user select a printer and options. When the user finishes the dialog various signals will be
 * emitted by the PrintOperation for you to handle, the main one being draw_page. You should then
 * render the page on the provided PrintContext using Cairo.
 *
 * @ingroup Printing
 */
class GTKMM_API PrintOperation :
  public Glib::Object,
  public PrintOperationPreview
{
  _CLASS_GOBJECT(PrintOperation, GtkPrintOperation, GTK_PRINT_OPERATION, Glib::Object, GObject, , , GTKMM_API)
  _IMPLEMENTS_INTERFACE(PrintOperationPreview)

protected:

  _CTOR_DEFAULT

public:
  _WRAP_ENUM(Result, GtkPrintOperationResult, decl_prefix GTKMM_API)
  _WRAP_ENUM(Action, GtkPrintOperationAction, decl_prefix GTKMM_API)

  _WRAP_CREATE()

  _WRAP_METHOD(void set_default_page_setup(const Glib::RefPtr<PageSetup>& default_page_setup),
               gtk_print_operation_set_default_page_setup)

  _WRAP_METHOD(Glib::RefPtr<PageSetup> get_default_page_setup(), gtk_print_operation_get_default_page_setup, refreturn)
  _WRAP_METHOD(Glib::RefPtr<const PageSetup> get_default_page_setup() const, gtk_print_operation_get_default_page_setup, refreturn, constversion)

  _WRAP_METHOD(void set_print_settings(const Glib::RefPtr<PrintSettings>& print_settings),
               gtk_print_operation_set_print_settings)

  _WRAP_METHOD(Glib::RefPtr<PrintSettings> get_print_settings(), gtk_print_operation_get_print_settings, refreturn)
  _WRAP_METHOD(Glib::RefPtr<const PrintSettings> get_print_settings() const, gtk_print_operation_get_print_settings, refreturn, constversion)

  _WRAP_METHOD(void set_job_name(const Glib::ustring& job_name), gtk_print_operation_set_job_name)
  _WRAP_METHOD(void set_n_pages(int n_pages), gtk_print_operation_set_n_pages)
  _WRAP_METHOD(void set_current_page(int current_page), gtk_print_operation_set_current_page)
  _WRAP_METHOD(void set_use_full_page(bool use_full_page = true), gtk_print_operation_set_use_full_page)
  _WRAP_METHOD(void set_unit(Unit unit), gtk_print_operation_set_unit)
  _WRAP_METHOD(void set_export_filename(const std::string& filename), gtk_print_operation_set_export_filename)
  _WRAP_METHOD(void set_track_print_status(bool track_status = true), gtk_print_operation_set_track_print_status)
  _WRAP_METHOD(void set_show_progress (bool show_progress = true), gtk_print_operation_set_show_progress)
  _WRAP_METHOD(void set_allow_async(bool allow_async = true), gtk_print_operation_set_allow_async)
  _WRAP_METHOD(void set_custom_tab_label(const Glib::ustring& label), gtk_print_operation_set_custom_tab_label)

  #m4 _CONVERSION(`GtkPrintOperationAction',`Action',`($2)$3')

  /** See the run() method that takes both action and parent parameters.
   */
  Result run(Action action = Action::PRINT_DIALOG);

  _WRAP_METHOD(Result run(Action action, Window& parent), gtk_print_operation_run, errthrow)

  _WRAP_METHOD(PrintStatus get_status() const, gtk_print_operation_get_status)
  _WRAP_METHOD(Glib::ustring get_status_string() const, gtk_print_operation_get_status_string)
  _WRAP_METHOD(void cancel(), gtk_print_operation_cancel)
  _WRAP_METHOD(bool is_finished() const, gtk_print_operation_is_finished)

  _WRAP_METHOD(void draw_page_finish(), gtk_print_operation_draw_page_finish)
  _WRAP_METHOD(void set_defer_drawing(), gtk_print_operation_set_defer_drawing)


  _WRAP_METHOD(void set_support_selection(bool support_selection = true), gtk_print_operation_set_support_selection)
  _WRAP_METHOD(bool get_support_selection() const, gtk_print_operation_get_support_selection)
  _WRAP_METHOD(void set_has_selection(bool has_selection = true), gtk_print_operation_set_has_selection)
  _WRAP_METHOD(bool get_has_selection() const, gtk_print_operation_get_has_selection)

  _WRAP_METHOD(void set_embed_page_setup(bool embed = true), gtk_print_operation_set_embed_page_setup)
  _WRAP_METHOD(bool get_embed_page_setup() const, gtk_print_operation_get_embed_page_setup)
  _WRAP_METHOD(int get_n_pages_to_print() const, gtk_print_operation_get_n_pages_to_print)

  _IGNORE(gtk_print_run_page_setup_dialog, gtk_print_run_page_setup_dialog_async)
  _IGNORE(gtk_print_operation_get_error)

  _WRAP_SIGNAL(void done(Result result), "done")

  #m4 _CONVERSION(`GtkPrintContext*',`const Glib::RefPtr<PrintContext>&',`Glib::wrap($3, true)')
  _WRAP_SIGNAL(void begin_print(const Glib::RefPtr<PrintContext>& context), "begin_print")
  _WRAP_SIGNAL(bool paginate(const Glib::RefPtr<PrintContext>& context), "paginate")

  #m4 _CONVERSION(`GtkPageSetup*',`const Glib::RefPtr<PageSetup>&',`Glib::wrap($3, true)')
  _WRAP_SIGNAL(void request_page_setup(const Glib::RefPtr<PrintContext>& context, int page_no, const Glib::RefPtr<PageSetup>& setup), "request_page_setup")
  _WRAP_SIGNAL(void draw_page(const Glib::RefPtr<PrintContext>& context, int page_nr), "draw_page")
  _WRAP_SIGNAL(void end_print(const Glib::RefPtr<PrintContext>& context), "end_print")
  _WRAP_SIGNAL(void status_changed(), "status_changed")

  #m4 _CONVERSION(`Widget*',`GObject*',`G_OBJECT(Glib::unwrap($3))')
  #m4 _CONVERSION(`GObject*',`Widget*',`Glib::wrap(GTK_WIDGET($3))')
  _WRAP_SIGNAL(Widget* create_custom_widget(), "create_custom_widget")

  _WRAP_SIGNAL(void custom_widget_apply(Widget* widget), "custom_widget_apply")

  #m4 _CONVERSION(`GtkPrintOperationPreview*', `const Glib::RefPtr<PrintOperationPreview>&', `Glib::wrap($3, true)')
  #m4 _CONVERSION(`const Glib::RefPtr<PrintOperationPreview>&', `GtkPrintOperationPreview*', `(Glib::unwrap($3))')

  #m4 _CONVERSION(`Window*',`GtkWindow*',`Glib::unwrap($3)')

  //TODO: This is causing crashes. Is it still causing crashes? murrayc.
  _WRAP_SIGNAL(bool preview(const Glib::RefPtr<PrintOperationPreview>& preview, const Glib::RefPtr<PrintContext>& context, Window* parent), "preview")

  #m4 _CONVERSION(`GtkPrintSettings*', `const Glib::RefPtr<PrintSettings>&', `Glib::wrap($3, true)')
  _WRAP_SIGNAL(void update_custom_widget(Widget* widget, const Glib::RefPtr<PageSetup>& setup, const Glib::RefPtr<PrintSettings>& settings), "update_custom_widget")

  _WRAP_PROPERTY("default_page_setup", Glib::RefPtr<PageSetup>)
  _WRAP_PROPERTY("print_settings", Glib::RefPtr<PrintSettings>)
  _WRAP_PROPERTY("job_name", Glib::ustring)
  _WRAP_PROPERTY("n_pages", int)
  _WRAP_PROPERTY("current_page", int)
  _WRAP_PROPERTY("use_full_page", bool)
  _WRAP_PROPERTY("track-print-status", bool)
  _WRAP_PROPERTY("unit", Unit)
  _WRAP_PROPERTY("show_progress", bool)
  _WRAP_PROPERTY("allow_async", bool)
  _WRAP_PROPERTY("export_filename", std::string)
  _WRAP_PROPERTY("status", PrintStatus)
  _WRAP_PROPERTY("status_string", Glib::ustring)
  _WRAP_PROPERTY("custom_tab_label", Glib::ustring)
  _WRAP_PROPERTY("support-selection", bool)
  _WRAP_PROPERTY("has-selection", bool)
  _WRAP_PROPERTY("embed-page-setup", bool)
  _WRAP_PROPERTY("n-pages-to-print", int)
};

  //TODO: Make these static members of a class instead of non-class functions?

  //Note: gtk_run_page_setup_dialog() can take a null page_setup object, but the application should always
  //store and reuse a page_setup object, so I see no need to provide an overload for that. murrayc.
  //TODO: The parent parameter may also be null, so maybe we should add an overload for that.

  /** Runs a page setup dialog, letting the user modify the values from page_setup.
   * If the user cancels the dialog, the returned PageSetup is identical to that passed in @a page_setup,
   * otherwise it contains the modifications done in the dialog.
   *
   * Note that this function may use a recursive mainloop to show the page setup dialog.
   * See run_page_setup_dialog_async() if this is a problem.
   *
   * @param parent Transient parent.
   * @param page_setup An existing GtkPageSetup.
   * @param print_settings Print settings.
   * @result A new PageSetup object.
   *
   * @since 2.10
   */
  GTKMM_API
  Glib::RefPtr<PageSetup> run_page_setup_dialog(Window& parent,
                                                const Glib::RefPtr<const PageSetup>& page_setup,
                                                const Glib::RefPtr<const PrintSettings>& print_settings);

  /** Runs a page setup dialog, letting the user modify the values from page_setup.
   * If the user cancels the dialog, the returned PageSetup is identical to that passed in @a page_setup,
   * otherwise it contains the modifications done in the dialog.
   *
   * Note that this function may use a recursive mainloop to show the page setup dialog.
   * See run_page_setup_dialog_async() if this is a problem.
   *
   * @param parent Transient parent.
   * @param print_settings Print settings.
   * @result A new PageSetup object.
   *
   * @since 2.22
   */
  GTKMM_API
  Glib::RefPtr<PageSetup> run_page_setup_dialog(Window& parent,
                                                const Glib::RefPtr<const PrintSettings>& print_settings);

  /** For example,
   * void on_setup_done(const Glib::RefPtr<PageSetup>& page_setup);
   */
  typedef sigc::slot<void(const Glib::RefPtr<PageSetup>&)> SlotPrintSetupDone;

  /** Runs a page setup dialog, letting the user modify the values from page_setup.
   *
   * In contrast to run_page_setup_dialog(), this function returns after showing the
   * page setup dialog on platforms that support this, and calls the @a slot from a
   * signal handler for the ::response signal of the dialog.
   *
   * @param parent Transient parent.
   * @param page_setup An existing GtkPageSetup.
   * @param print_settings Print settings.
   * @param slot
   *
   * @since 2.10
   */
  GTKMM_API
  void run_page_setup_dialog_async(Window& parent,
                                   const Glib::RefPtr<const PageSetup>& page_setup,
                                   const Glib::RefPtr<const PrintSettings>& print_settings,
                                   const SlotPrintSetupDone& slot);

  /** Runs a page setup dialog, letting the user modify the values from page_setup.
   *
   * In contrast to run_page_setup_dialog(), this function returns after showing the
   * page setup dialog on platforms that support this, and calls the @a slot from a
   * signal handler for the ::response signal of the dialog.
   *
   * @param parent Transient parent.
   * @param print_settings Print settings.
   * @param slot
   *
   * @since 2.22
   */
  GTKMM_API
  void run_page_setup_dialog_async(Window& parent,
                                   const Glib::RefPtr<const PrintSettings>& print_settings,
                                   const SlotPrintSetupDone& slot);

} // namespace Gtk
