/* Copyright (C) 2006 to 2019 Chris Vine

The library comprised in this file or of which this file is part is
distributed by Chris Vine under the GNU Lesser General Public
License as follows:

   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, version 2.1, for more details.

   You should have received a copy of the GNU Lesser General Public
   License, version 2.1, along with this library (see the file LGPL.TXT
   which came with this source code package in the c++-gtk-utils
   sub-directory); if not, write to the Free Software Foundation, Inc.,
   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

*/

#ifndef CGU_FILE_PRINTMANAGER_H
#define CGU_FILE_PRINTMANAGER_H

#include <string>

#include <c++-gtk-utils/cgu_config.h>

#ifdef CGU_USE_GTK
#include <gtk/gtk.h>
#include <gdk/gdk.h>
#if GTK_CHECK_VERSION(2,14,0)
#include <gtk/gtkunixprint.h>
#else
#include <gtk/gtkprintunixdialog.h>
#include <gtk/gtkprintjob.h>
#include <gtk/gtkprinter.h>
#endif
#endif

#include <c++-gtk-utils/window.h>
#include <c++-gtk-utils/notifier.h>
#include <c++-gtk-utils/mutex.h>
#include <c++-gtk-utils/gobj_handle.h>
#include <c++-gtk-utils/intrusive_ptr.h>
#include <c++-gtk-utils/emitter.h>


namespace Cgu {

#if defined(DOXYGEN_PARSING) || defined(CGU_USE_GTK)

/**
 * @class FilePrintDialog file_print_manager.h c++-gtk-utils/file_print_manager.h
 * @brief A print dialog class for FilePrintManager.
 * @sa FilePrintManager
 *
 * This class is not compiled into the library if the library is built
 * with the \--without-gtk configuration option.
 */

class FilePrintDialog: public WinBase {
protected:
/**
 * Closes the dialog, cleans up and emits the rejected Emitter object.
 * It will not throw, even if a method connected to the rejected
 * Emitter object throws (the exception is caught and reported in
 * order to prevent it trying to propagate through the GTK event
 * system).
 *
 * This function is only used where the library is compiled against
 * GTK-4.  Where this library is compiled against GTK-2 or GTK-3, the
 * on_delete_event() function is used instead.
 *
 * Since 2.2.16
 */
#if defined(DOXYGEN_PARSING) || GTK_CHECK_VERSION(3,94,0)
  virtual void on_close_request();
#endif

/**
 * Closes the dialog, cleans up and emits the rejected Emitter object.
 * It will not throw, even if a method connected to the rejected
 * Emitter object throws (the exception is caught and reported in
 * order to prevent it trying to propagate through the GTK event
 * system).
 *
 * This function is only used where the library is compiled against
 * GTK-2 and GTK-3.  Where this library is compiled against GTK-4, the
 * on_close_request() function is used instead.
 */
#if defined(DOXYGEN_PARSING) || !GTK_CHECK_VERSION(3,94,0)
  virtual void on_delete_event();
#endif

public:
#ifndef DOXYGEN_PARSING
  // this helper class avoids exposing GObject callbacks with C
  // linkage to the global namespace
  class CB;
  friend class CB;
#endif

/**
 * The accepted Emitter object emits if the user choses OK in the
 * print dialog.  Emission of accepted by this class will not throw,
 * even if a method connected to the accepted Emitter object throws
 * (the exception is caught and reported in order to prevent it trying
 * to propagate through the GTK event system).
 */
  Emitter accepted;

/**
 * The rejected Emitter object emits if the user choses Cancel in the
 * print dialog.  Emission of rejected by this class will not throw,
 * even if a method connected to the rejected Emitter object throws
 * (the exception is caught and reported in order to prevent it trying
 * to propagate through the GTK event system).
 */
  Emitter rejected;

/**
 * Gets the currently selected printer from the print dialog.  It will
 * not throw.  It must be called in the thread in which the main GTK
 * event loop runs.
 * @return The currently selected printer in the print dialog.  The
 * return value is owned by GTK - do not unreference it.
 */
  GtkPrinter* get_printer() const;

/**
 * Gets the print settings from the print dialog.  It will not throw.
 * It must be called in the thread in which the main GTK event loop
 * runs.
 * @return The currently selected printer in the print dialog.
 * Ownership is taken of the return value, by GobjHandle.
 */
  GobjHandle<GtkPrintSettings> get_settings() const;

/**
 * Gets the printer page set-up from the print dialog.  It will not
 * throw.  It must be called in the thread in which the main GTK
 * event loop runs.
 * @return The printer page set-up in the print dialog.  The return
 * value is owned by GTK - do not unreference it.
 */
  GtkPageSetup* get_page_setup() const;

/**
 * The constructor will not throw.  A FIlePrintDialog object must be
 * created in the thread in which the main GTK event loop runs.
 * @param parent The parent of the print dialog (NULL may be passed).
 * @param print_settings The print settings from a previous print job
 * (if any, NULL may be passed if none).
 * @param caption Window caption (optional, NULL may be passed).
 * @param window_icon An icon which will comprise the window icon
 * (optional \- NULL or nullptr may be passed).  For GTK-2 and GTK-3
 * this is of type GdkPixbuf*.  For GTK-4 this is of type const char*,
 * representing an icon theme name rather than the icon itself.
 */
#ifdef DOXYGEN_PARSING
  FilePrintDialog(GtkWindow* parent, GtkPrintSettings* print_settings = 0,
		  const char* caption = 0, GdkPixbuf*/const char* window_icon = 0);
#elif GTK_CHECK_VERSION(3,96,0)
  FilePrintDialog(GtkWindow* parent, GtkPrintSettings* print_settings = 0,
		  const char* caption = 0, const char* window_icon = 0);
#else
  FilePrintDialog(GtkWindow* parent, GtkPrintSettings* print_settings = 0,
		  const char* caption = 0, GdkPixbuf* window_icon = 0);
#endif

  /* we inherit glib memory slice functions from WinBase */
};


/**
 * @class FilePrintManager file_print_manager.h c++-gtk-utils/file_print_manager.h
 * @brief A class to print a file using the GTK print system.
 *
 * The FilePrintManager class prints a file printable by the print
 * system (eg lpr or CUPS) using the GTK printer interface.  All
 * print systems on Unix-like systems will print Postscript (PS)
 * files.  Some may also print PDF files.  To obtain a
 * FilePrintManager object, call FilePrintManager::create_manager().
 * FilePrintManager::set_filename() passes the name of the file to be
 * printed.  To print the file, call FilePrintManager::print().  If
 * FilePrintManager::print() returns true, the FilePrintManager class
 * will unlink() (ie delete) the file to be printed when it has been
 * finished with if true is passed as the second argument of
 * FilePrintManager::set_filename(), even if printing failed.
 *
 * Once FilePrintManager::print() has been called, the
 * FilePrintManager class owns a reference to itself and so manages
 * its own lifetime - so once that method has been called it doesn't
 * matter if the IntrusivePtr object returned by
 * FilePrintManager::create_manager() goes out of scope (however, once
 * FilePrintManager::print() has been called, the FilePrintManager
 * object will not be deleted until both printing has completed or
 * failed AND the IntrusivePtr object returned by
 * FilePrintManager::create_manager() has gone out of scope or has
 * been reset()).
 *
 * If the print dialog which is displayed on calling
 * FilePrintManager::print() is to display a window icon, then pass
 * FilePrintManager::create_manager() a GobjHandle<GdkPixbuf> icon as
 * the second argument.
 *
 * Normally, a user would probably only want to use any one
 * FilePrintManager object to print a single print job (it has been
 * designed with that in mind).  Nevertheless, if a reference to the
 * object is kept alive via an active IntrusivePtr object, it can be
 * used to print more than one print job.  However, if that is done it
 * is not possible to call FilePrintManager::print() or
 * FilePrintManager::set_filename() until the previous print job (if
 * any) has been dispatched to the GTK print system (or cancelled).
 * If the FilePrintManager object is not ready because it is in the
 * middle of handling an earlier print job, then the call to
 * FilePrintManager::print() or FilePrintManager::set_filename() will
 * return false; if however the new print job was successfully started
 * or file name successfully set, they will return true.  It is not
 * necessary to check the return value of these methods for the first
 * print job despatched by any one FilePrintManager object.
 *
 * For printing plain text (say, from a GtkTextBuffer class, or from a
 * plain text file), see the TextPrintManager class.
 *
 * This class is not compiled into the library if the library is built
 * with the \--without-gtk configuration option.
 */

class FilePrintManager: public IntrusiveLockCounter {
  Thread::Mutex mutex;
  GtkWindow* parent_p;
  std::string caption;
#if GTK_CHECK_VERSION(3,96,0)
  std::string window_icon;
#else
  GobjHandle<GdkPixbuf> window_icon;
#endif
  bool manage;
  std::string filename;
  FilePrintDialog* dialog_p;
  Notifier print_notifier;
  bool ready;

  static GobjHandle<GtkPrintSettings> print_settings_h;

  void show_dialog();
  void print_file();
  void print_cancel();
  void clean_up();
  // private constructor
  FilePrintManager() {}
public:
#ifndef DOXYGEN_PARSING
  // this helper class avoids exposing GObject callbacks with C
  // linkage to the global namespace
  class CB;
  friend class CB;
#endif
/**
 * This class cannot be copied: it is intended to be held by
 * IntrusivePtr.  The copy constructor is deleted.
 */
  FilePrintManager(const FilePrintManager&) = delete;

/**
 * This class cannot be copied: it is intended to be held by
 * IntrusivePtr.  The assignment operator is deleted.
 */
  FilePrintManager& operator=(const FilePrintManager&) = delete;

/**
 * Creates a new FilePrintManager object.  No GTK/GDK functions are
 * called by this method, and it is thread safe provided that, if it
 * is called in a thread other than the one in which the GTK event
 * loop runs, Cgu::Notifier::init() has previously been called in the
 * GTK event loop thread.
 * @param parent The parent of the print dialog which will be created
 * by the FilePrintManager object (optional, NULL may be passed).
 * @param caption Window caption of the print dialog which will be
 * created by the FilePrintManager object (optional, an empty string
 * may be passed).
 * @param window_icon An icon which will comprise the window icon of
 * the print dialog which will be created by the FilePrintManager
 * object (optional, an empty GobjHandle object or empty string may be
 * passed).  For GTK-2 and GTK-3 this is of type
 * GobjHandle<GdkPixbuf>.  For GTK-4 this is of type std::string,
 * representing an icon theme name rather than the icon itself.
 * @exception std::bad_alloc This method might throw std::bad_alloc if
 * memory is exhausted and the system throws in that case.
 * @exception Cgu::Thread::MutexError This method might throw
 * Cgu::Thread::MutexError if initialisation of the contained mutex
 * fails.  (It is often not worth checking for this, as it means
 * either memory is exhausted or pthread has run out of other
 * resources to create new mutexes.)
 * @exception Cgu::PipeError This method might throw Cgu::PipeError if
 * the contained Notifier object is the first Notifier object in the
 * program to be constructed and Cgu::Notifier::init() has not
 * previously been called.
 */
#ifdef DOXYGEN_PARSING
  static Cgu::IntrusivePtr<Cgu::FilePrintManager> create_manager(GtkWindow* parent = 0,
								 const std::string& caption = "",
								 const GobjHandle<GdkPixbuf>&/const std::string& window_icon = GobjHandle<GdkPixbuf>(0)/"");
#elif GTK_CHECK_VERSION(3,96,0)
  static Cgu::IntrusivePtr<Cgu::FilePrintManager> create_manager(GtkWindow* parent = 0,
								 const std::string& caption = "",
								 const std::string& window_icon = "");
#else
  static Cgu::IntrusivePtr<Cgu::FilePrintManager> create_manager(GtkWindow* parent = 0,
								 const std::string& caption = "",
								 const GobjHandle<GdkPixbuf>& window_icon = GobjHandle<GdkPixbuf>(0));
#endif

/**
 * Sets the filename of the file to be printed.  This method is
 * thread-safe and may be called in any thread.  No GTK/GDK functions
 * are called.
 * @param filename The filename of the file to be printed.
 * @param manage_file If this argument is set to true, then if print()
 * returns true, after printing succeeds or fails the file being
 * printed will be deleted.
 * @return Returns true, unless the same FilePrintManager object is
 * being used to print more than one print job and when this method is
 * called it is already printing another print job (in which case it
 * returns false).
 * @exception std::bad_alloc This method might throw std::bad_alloc if
 * memory is exhausted and the system throws in that case.
 */
  bool set_filename(const char* filename, bool manage_file = false);

/**
 * Prints the file set with set_filename().  This method is
 * thread-safe and may be called in any thread (it hands off its work
 * to the main program thread via a Cgu::Notifier object), unless the
 * program by which it is called calls GTK directly in more than one
 * thread and thus employs gdk_threads_enter()/gdk_threads_leave()
 * (rather than, say, Cgu::Notifier or Cgu::Callback::post()), in
 * which case it must be called in the main GUI thread only and
 * surrounded by gdk_threads_enter()/gdk_threads_leave() if called
 * otherwise than in a GTK signal handler.  (The best approach
 * however is for a program only to address GTK/GDK in the main
 * program thread, for which purpose this library provides various
 * functions and classes for inter-thread communication, such as
 * Cgu::Notifier and Cgu::Callback::post().)
 * @return Returns true, unless the same FilePrintManager object is
 * being used to print more than one print job and when this method is
 * called it is already printing another print job (in which case it
 * returns false).
 * @exception std::bad_alloc This method might throw std::bad_alloc if
 * memory is exhausted and the system throws in that case, but only if
 * it is called in the main program thread.  Otherwise it will not
 * throw.
 */
  bool print();

/**
 * The destructor will not throw.  It is thread safe (the
 * FilePrintManager object may be destroyed in any thread), and does
 * not call any GTK/GDK functions.
 */
  ~FilePrintManager();

#ifdef CGU_USE_GLIB_MEMORY_SLICES_NO_COMPAT
  CGU_GLIB_MEMORY_SLICES_FUNCS
#endif
};

#endif // CGU_USE_GTK

} // namespace Cgu

#endif // CGU_FILE_PRINTMANAGER_H
