File Coverage

blib/lib/Renard/Curie/App.pm
Criterion Covered Total %
statement 4 6 66.6
branch n/a
condition n/a
subroutine 2 2 100.0
pod n/a
total 6 8 75.0


line stmt bran cond sub pod time code
1 3     3   1261 use Renard::Curie::Setup;
  3         8  
  3         16  
2             package Renard::Curie::App;
3             # ABSTRACT: A document viewing application
4             $Renard::Curie::App::VERSION = '0.002';
5 3     3   1852 use Gtk3 -init;
  0            
  0            
6             use Cairo;
7             use Glib::Object::Introspection;
8             use Glib 'TRUE', 'FALSE';
9              
10             use URI::file;
11              
12             use Moo 2.001001;
13              
14             use Renard::Curie::Helper;
15             use Renard::Curie::Model::Document::PDF;
16             use Renard::Curie::Component::PageDrawingArea;
17             use Renard::Curie::Component::Outline;
18             use Renard::Curie::Component::MenuBar;
19             use Renard::Curie::Component::LogWindow;
20             use Renard::Curie::Component::FileChooser;
21             use Renard::Curie::Component::AccelMap;
22              
23             use Log::Any::Adapter;
24             use MooX::Role::Logger ();
25             use Getopt::Long::Descriptive;
26              
27             use Renard::Curie::Types qw(InstanceOf Path Str DocumentModel);
28             use Function::Parameters;
29              
30             use constant {
31             DND_TARGET_URI_LIST => 0,
32             DND_TARGET_TEXT => 1,
33             };
34              
35             has window => ( is => 'lazy' );
36              
37             method _build_window() :ReturnType(InstanceOf['Gtk3::Window']) {
38             my $window = $self->builder->get_object('main-window');
39             }
40              
41             has page_document_component => (
42             is => 'rw',
43             isa => InstanceOf['Renard::Curie::Component::PageDrawingArea'],
44             predicate => 1, # has_page_document_component
45             clearer => 1 # clear_page_document_component
46             );
47              
48             has menu_bar => (
49             is => 'rw',
50             isa => InstanceOf['Renard::Curie::Component::MenuBar'],
51             );
52              
53             has outline => (
54             is => 'rw',
55             isa => InstanceOf['Renard::Curie::Component::Outline'],
56             );
57              
58             has log_window => (
59             is => 'rw',
60             isa => InstanceOf['Renard::Curie::Component::LogWindow'],
61             );
62              
63             has content_box => (
64             is => 'rw',
65             isa => InstanceOf['Gtk3::Box'],
66             );
67              
68             classmethod setup_gtk() {
69             # stub out the GDL loading for now. Docking is not yet used.
70             ##Glib::Object::Introspection->setup(
71             ##basename => 'Gdl',
72             ##version => '3',
73             ##package => 'Gdl', );
74             }
75              
76             method setup_window() {
77             my $menu = Renard::Curie::Component::MenuBar->new( app => $self );
78             $self->menu_bar( $menu );
79             $self->builder->get_object('application-vbox')
80             ->pack_start( $menu, FALSE, TRUE, 0 );
81              
82             $self->content_box( Gtk3::Box->new( 'horizontal', 0 ) );
83             $self->builder->get_object('application-vbox')
84             ->pack_start( $self->content_box, TRUE, TRUE, 0 );
85              
86             $self->outline( Renard::Curie::Component::Outline->new( app => $self ) );
87             $self->content_box->pack_start( $self->outline , FALSE, TRUE, 0 );
88              
89             my $log_win = Renard::Curie::Component::LogWindow->new( app => $self );
90             Log::Any::Adapter->set('+Renard::Curie::Log::Any::Adapter::LogWindow',
91             log_window => $log_win );
92             $self->log_window( $log_win );
93              
94             Renard::Curie::Component::AccelMap->new( app => $self );
95             }
96              
97             method setup_dnd() {
98             $self->content_box->drag_dest_set('all', [], 'copy');
99             my $target_list = Gtk3::TargetList->new([
100             Gtk3::TargetEntry->new( 'text/uri-list', 0, DND_TARGET_URI_LIST ),
101             Gtk3::TargetEntry->new( 'text/plain' , 0, DND_TARGET_TEXT )
102             ]);
103             $self->content_box->drag_dest_set_target_list($target_list);
104              
105             $self->content_box->signal_connect('drag-data-received' =>
106             \&on_drag_data_received_cb, $self );
107             }
108              
109             method run() {
110             $self->window->show_all;
111             $self->_logger->info("starting the Gtk main event loop");
112             Gtk3::main;
113             }
114              
115             method BUILD(@) {
116             $self->setup_gtk;
117              
118             $self->setup_window;
119             $self->setup_dnd;
120              
121             $self->window->signal_connect(
122             destroy => \&on_application_quit_cb, $self );
123             $self->window->set_default_size( 800, 600 );
124             }
125              
126             method process_arguments() {
127             my ($opt, $usage) = describe_options(
128             "%c %o <filename>",
129             [ 'version', "print version and exit" ],
130             [ 'short-version', "print just the version number (if exists) and exit" ],
131             [ 'help', "print usage message and exit" ],
132             );
133              
134             print($usage->text), exit if $opt->help;
135              
136             if($opt->version) {
137             say("Project Renard Curie @{[ _get_version() ]}");
138             say("Distributed under the same terms as Perl 5.");
139             exit;
140             }
141              
142             if($opt->short_version) {
143             say(_get_version()), exit
144             }
145              
146             my $pdf_filename = shift @ARGV;
147              
148             if( $pdf_filename ) {
149             $self->_logger->infof("opening the file %s", $pdf_filename);
150             $self->open_pdf_document( $pdf_filename );
151             }
152             }
153              
154             fun _get_version() :ReturnType(Str) {
155             return $Renard::Curie::App::VERSION // 'dev'
156             }
157              
158             method main() {
159             $self = __PACKAGE__->new unless ref $self;
160             $self->process_arguments;
161             $self->run;
162             }
163              
164             method open_pdf_document( (Path->coercibles) $pdf_filename ) {
165             $pdf_filename = Path->coerce( $pdf_filename );
166             if( not -f $pdf_filename ) {
167             Renard::Curie::Error::IO::FileNotFound
168             ->throw("PDF filename does not exist: $pdf_filename");
169             }
170              
171             my $doc = Renard::Curie::Model::Document::PDF->new(
172             filename => $pdf_filename,
173             );
174              
175             # set window title
176             $self->window->set_title( $pdf_filename );
177              
178             $self->open_document( $doc );
179             }
180              
181             method open_document( (DocumentModel) $doc ) {
182             if( $self->has_page_document_component ) {
183             $self->content_box->remove( $self->page_document_component );
184             $self->clear_page_document_component;
185             }
186             my $pd = Renard::Curie::Component::PageDrawingArea->new(
187             document => $doc,
188             );
189             $self->outline->update( $doc );
190             $self->page_document_component($pd);
191             $self->content_box->pack_start( $pd, TRUE, TRUE, 0 );
192             $pd->show_all;
193             }
194              
195             # Callbacks {{{
196             callback on_open_file_dialog_cb( $event, $self ) {
197             my $file_chooser = Renard::Curie::Component::FileChooser->new( app => $self );
198             my $dialog = $file_chooser->get_open_file_dialog_with_filters;
199              
200             my $result = $dialog->run;
201              
202             if ( $result eq 'accept' ) {
203             my $filename = $dialog->get_filename;
204             $dialog->destroy;
205             $self->open_pdf_document($filename);
206             } else {
207             $dialog->destroy;
208             }
209             }
210              
211             callback on_application_quit_cb( $event, $self ) {
212             Gtk3::main_quit;
213             }
214              
215             callback on_drag_data_received_cb( $widget, $context, $x, $y, $data, $info, $time, $app ) {
216             if( $info == DND_TARGET_URI_LIST ) {
217             my @uris = @{ $data->get_uris };
218             my $pdf_filename = URI->new($uris[0])->file;
219             $app->open_pdf_document( $pdf_filename );
220             }
221             }
222             # }}}
223              
224             with qw(
225             Renard::Curie::Component::Role::FromBuilder
226             Renard::Curie::Component::Role::UIFileFromPackageName
227             MooX::Role::Logger
228             );
229              
230             1;
231              
232             __END__
233              
234             =pod
235              
236             =encoding UTF-8
237              
238             =head1 NAME
239              
240             Renard::Curie::App - A document viewing application
241              
242             =head1 VERSION
243              
244             version 0.002
245              
246             =head1 EXTENDS
247              
248             =over 4
249              
250             =item * L<Moo::Object>
251              
252             =back
253              
254             =head1 CONSUMES
255              
256             =over 4
257              
258             =item * L<MooX::Role::Logger>
259              
260             =item * L<Renard::Curie::Component::Role::FromBuilder>
261              
262             =item * L<Renard::Curie::Component::Role::UIFileFromPackageName>
263              
264             =back
265              
266             =head1 FUNCTIONS
267              
268             =head2 _get_version
269              
270             fun _get_version() :ReturnType(Str)
271              
272             Returns the version of the application if there is one.
273             Otherwise returns the C<Str> C<'dev'> to indicate that this is a
274             development version.
275              
276             =head2 main
277              
278             fun main()
279              
280             Application entry point.
281              
282             =head1 ATTRIBUTES
283              
284             =head2 window
285              
286             A L<Gtk3::Window> that contains the main window for the application.
287              
288             =head2 page_document_component
289              
290             A L<Renard::Curie::Component::PageDrawingArea> that holds the currently
291             displayed document.
292              
293             =over 4
294              
295             =item *
296              
297             Predicate: C<has_page_document_component>
298              
299             =item *
300              
301             Clearer: C<clear_page_document_component>
302              
303             =back
304              
305             =head2 menu_bar
306              
307             A L<Renard::Curie::Component::MenuBar> for the application's menu-bar.
308              
309             =head2 outline
310              
311             A L<Renard::Curie::Component::Outline> which makes up the outline sidebar for
312             this window.
313              
314             =head2 log_window
315              
316             A L<Renard::Curie::Component::LogWindow> for the application's logging.
317              
318             =head2 content_box
319              
320             A horizontal L<Gtk3::Box> which is used to split the main application area into
321             two different regions.
322              
323             The left region contains L</outline> and the right region contains L</page_document_component>.
324              
325             =head1 CLASS METHODS
326              
327             =head2 setup_gtk
328              
329             classmethod setup_gtk()
330              
331             Sets up any of the L<Glib::Object::Introspection>-based libraries needed for
332             the application.
333              
334             Currently loads nothing, but will load the Gnome Docking Library (C<libgdl>) in
335             the future.
336              
337             =head1 METHODS
338              
339             =head2 setup_window
340              
341             method setup_window()
342              
343             Sets up components that make up the window shell for the application
344             including:
345              
346             =over 4
347              
348             =item *
349              
350             L</menu_bar>
351              
352             =item *
353              
354             L</content_box>
355              
356             =item *
357              
358             L</log_window>
359              
360             =back
361              
362             =head2 setup_dnd
363              
364             method setup_dnd()
365              
366             Setup drag and drop.
367              
368             =head2 run
369              
370             method run()
371              
372             Displays L</window> and starts the L<Gtk3> event loop.
373              
374             =head2 BUILD
375              
376             method BUILD
377              
378             Initialises the application and sets up signals.
379              
380             =head2 process_arguments
381              
382             method process_arguments()
383              
384             Processes arguments given in C<@ARGV>.
385              
386             =head2 open_pdf_document
387              
388             method open_pdf_document( (Path->coercibles) $pdf_filename )
389              
390             Opens a PDF file stored on the disk.
391              
392             =head2 open_document
393              
394             method open_document( (DocumentModel) $doc )
395              
396             Sets the document for the application's L</page_document_component>.
397              
398             =head1 CALLBACKS
399              
400             =head2 on_open_file_dialog_cb
401              
402             callback on_open_file_dialog_cb( $event, $self )
403              
404             Callback that opens a L<Renard::Curie::Component::FileChooser> component.
405              
406             =head2 on_application_quit_cb
407              
408             callback on_application_quit_cb( $event, $self )
409              
410             Callback that stops the L<Gtk3> main loop.
411              
412             =head2 on_drag_data_received_cb
413              
414             on_drag_data_received_cb
415              
416             Whenever the drag and drop data is received by the application.
417              
418             =for Pod::Coverage has_page_document_component clear_page_document_component
419              
420             =head1 AUTHOR
421              
422             Project Renard
423              
424             =head1 COPYRIGHT AND LICENSE
425              
426             This software is copyright (c) 2016 by Project Renard.
427              
428             This is free software; you can redistribute it and/or modify it under
429             the same terms as the Perl 5 programming language system itself.
430              
431             =cut