Chapter 30. Print Dialog

Index

Using the print dialog
Another singleton example
Print dialog

printdialog.C demonstrates the basics of using a x::w::print_dialog. This dialog shows a list of printers, and each printer's options.

Note

Using this dialog requires linking with -lcxxwcups and -lcxxcups in addition to the default libraries. See the sample Makefile.am for more information.

/*
** Copyright 2018-2021 Double Precision, Inc.
** See COPYING for distribution information.
*/

#include "config.h"
#include <x/mpobj.H>
#include <x/exception.H>
#include <x/destroy_callback.H>
#include <x/ref.H>
#include <x/obj.H>
#include <x/appid.H>

#include <x/w/main_window.H>
#include <x/w/button.H>
#include <x/w/gridlayoutmanager.H>
#include <x/w/gridfactory.H>
#include <x/w/text_param.H>
#include <x/w/text_param_literals.H>

#include <x/w/file_dialog.H>
#include <x/w/print_dialog.H>
#include <x/mpobj.H>
#include <x/singletonptr.H>
#include <x/cups/job.H>

#include "close_flag.H"

#include <string>
#include <iostream>

std::string x::appid() noexcept
{
	return "printdialog.examples.w.libcxx.com";
}

// Our "application" object.

class my_appObj : virtual public x::obj {

public:

	// The main application window
	const x::w::main_window main_window;

	// The file_dialog used to select a file to print.

	const x::w::file_dialog select_file_dialog;

	// And the print dialog

	const x::w::print_dialog print_dialog;

	// Once the file dialog selects the file we save it and open the
	// print dialog. Once the print dialog's "Print" button gets clicked
	// we retrieve the filename and print it.

	// Currently there's no possibility for this std::string to be
	// accessed concurrently by multiple threads, and everything gets
	// correctly sequenced. However, let's not rely on that, and wrap it
	// with an mpobj.

	x::mpobj<std::string> selected_file;

	my_appObj(const x::w::main_window &main_window);

};

// The application object will be a singleton object.

typedef x::singletonptr<my_appObj> my_app;

static x::w::file_dialog create_select_file_dialog(const x::w::main_window &main_window)
{
	// The "Print" button in the main window shows the file dialog, first,
	// to select the file to print.

	x::w::file_dialog_config config{
		[](ONLY IN_THREAD,
		   const x::w::file_dialog &this_dialog,
		   const std::string &filename,
		   const x::w::busy &ignored)
		{
			// It's our responsibility to close the dialog.

			this_dialog->dialog_window->hide();

			my_app app;

			if (!app)
				return;

			// Need to remember the filename.

			app->selected_file=filename;
			app->print_dialog->initial_show();
		},

		[](ONLY IN_THREAD,
		   const auto &ignore)
		{
			std::cout << "File dialog cancelled." << std::endl;
		}};

	auto d=main_window->create_file_dialog
		({"file@printdialog.examples.w.libcxx.com", true},
		 config);

	d->dialog_window->set_window_title("Select the file to print");

	return d;
}

static x::w::print_dialog create_print_dialog(const x::w::main_window &main_window)
{
	// Once the file gets selected, the print dialog gets opened. Here
	// the print dialog object gets created in advance.

	x::w::print_dialog_config config{
		[]
		(const x::w::print_callback_info &info)
		{
			// The "Print" button was selected. The print dialog
			// takes care of closing itself. Our job is to
			// submit the print job. The print dialog helpfully
			// gives us the x::cups::job object, with all the
			// selected options set.
			//
			// All we have to do is specify the file to print
			// and submit the print job.

			my_app app;

			if (!app)
				return;

			info.job->add_document_file("file",
						    app->selected_file.get());

			std::ostringstream o;

			o << "Created print job "
			  << info.job->submit("printdialog");

			// This is not really an error message, I'm just too
			// lazy to formally create_ok_dialog().

			x::w::alert_message_config config;

			config.title="Job submitted";

			app->main_window->alert_message(o.str(), config);
		},
		[]
		(ONLY IN_THREAD)
		{
			std::cout << "Print dialog cancelled." << std::endl;
		}};

	auto d=main_window->create_print_dialog
		({"print@printdialog.examples.w.libcxx.com", true},
		 config);
	d->dialog_window->set_window_title("Select the printer");

	return d;
}

my_appObj::my_appObj(const x::w::main_window &main_window)
	: main_window{main_window},
	  select_file_dialog{create_select_file_dialog(main_window)},
	  print_dialog{create_print_dialog(main_window)}
{
}

///////////////////////////////////////////////////////////////////////////
//
// main_window creator function. Just a print button, with ample margins
// around it.

static inline void initialize_mainwindow(const x::w::gridfactory &f)
{
	// The main window is just a button. With plenty of padding.
	auto b=f->padding(20).create_button
		({
			x::w::text_decoration::underline,
			"P",
			x::w::text_decoration::none,
			"rint",
		},
		x::w::shortcut{"Alt-P"});

	// This is the only display element in the main window. Might as
	// well give it focus automatically.

	b->autofocus(true);

	// The button's callback opens the file dialog.

	b->on_activate([]
		       (ONLY IN_THREAD,
			const x::w::callback_trigger_t &trigger,
			const x::w::busy &ignore)
		       {
			       my_app app;

			       if (!app)
				       return;

			       app->select_file_dialog->dialog_window
				       ->show_all();
		       });
}

void testprintdialog()
{
	x::destroy_callback::base::guard guard;

	auto close_flag=close_flag_ref::create();

	x::ptr<my_appObj> app_ptr;

	auto main_window=x::w::main_window
		::create([&]
			 (const auto &main_window)
			 {
				 auto layout=main_window->gridlayout();

				 x::w::gridfactory factory=
					 layout->append_row();

				 initialize_mainwindow(factory);

				 app_ptr=x::ref<my_appObj>::create(main_window);
			 });

	main_window->set_window_title("Print something!");

	// Construct the application singleton object.
	//
	// This object holds additional references to the top level window and
	// its dialogs. This singleton object gets constructed in automatic
	// scope, so it goes out of scope and gets destroyed before
	// the main_window, and before the destructor guard, so all these
	// references will go away in an orderly fashion.

	my_app app{app_ptr};

	guard(main_window->connection_mcguffin());

	main_window->on_disconnect([]
				   {
					   exit(1);
				   });

	main_window->on_delete
		([close_flag]
		 (ONLY IN_THREAD,
		  const auto &ignore)
		 {
			 close_flag->close();
		 });

	main_window->show_all();

	x::mpcobj<bool>::lock lock{close_flag->flag};

	lock.wait([&] { return *lock; });
}

int main(int argc, char **argv)
{
	try {
		testprintdialog();
	} catch (const x::exception &e)
	{
		e->caught();
		exit(1);
	}
	return 0;
}

Using the print dialog

printdialog.C calls the x::w::main_window's create_print_dialog()'s method to create the print dialog in advance, during the program's initialization phase. Clicking the sole Print button in the window opens a file dialog first, this select the file to print. Afterwards the print dialog's initial_show() makes it visible.

initial_show() returns immediately, so it can be called from some other callback, in this case the file dialog's callback. create_print_dialog()'s x::w::print_dialog_config parameter specifies a callback that gets invoked in response to the Print button.

The callback receives a x:;w::print_callback_info parameter with a pre-configured x::cups::job based on the options selected in the print dialog. printdialog.C saves the file after it gets selected in the initially-shown file open dialog, then use the x::cups::job to submit the print job.