1 /*
2     dspdfviewer - Dual Screen PDF Viewer for LaTeX-Beamer
3     Copyright (C) 2012  Danny Edel <mail@danny-edel.de>
4 
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9 
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14 
15     You should have received a copy of the GNU General Public License along
16     with this program; if not, write to the Free Software Foundation, Inc.,
17     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19 
20 
21 #include "runtimeconfiguration.h"
22 
23 #include <boost/program_options.hpp>
24 #include <iostream>
25 #include <fstream>
26 #include <QCoreApplication>
27 
28 #ifndef DSPDFVIEWER_VERSION
29 #warning DSPDFVIEWER_VERSION was not set by the build system!
30 #define DSPDFVIEWER_VERSION "UNKNOWN"
31 #endif
32 
33 #ifndef I3WORKAROUND_SHELLCODE
34 /* This will get executed once both windows are created, provided
35  * the user passed --i3-workaround=true via command line or configuration file.
36  *
37  * You can override the shellcode by providing -DI3WORKAROUND_SHELLCODE="your shellcode"
38  * at the cmake step.
39  */
40 #define I3WORKAROUND_SHELLCODE "i3-msg '[class=\"Dspdfviewer\" window_role=\"Audience_Window\"] move to output right, fullscreen'"
41 #endif
42 
43 using namespace std;
44 using namespace boost::program_options;
45 
RuntimeConfiguration(int argc,const char * const * argv)46 RuntimeConfiguration::RuntimeConfiguration(int argc, const char* const * argv):
47 	m_useFullPage(false),
48 	m_showPresenterArea(true),
49         m_duplicate(false),
50 	m_showWallClock(true),
51 	m_showThumbnails(true),
52 	m_thumbnailPagePart(PagePart::FullPage),
53 	m_showPresentationClock(true),
54 	m_showSlideClock(true),
55 	m_filePath(),
56 	m_hyperlinkSupport(true),
57 	m_cacheToMemory(true),
58 	m_cacheSizeMegaBytes(0),
59 	m_useSecondScreen(true),
60 	m_i3workaround(false),
61 	m_prerenderPreviousPages(3),
62 	m_prerenderNextPages(10),
63 	m_bottomPaneHeightPercent(20)
64 {
65   options_description generic( tr("Generic options").toLocal8Bit().constData() );
66 
67 /** FIXME: Using .toLocal8Bit() [.constData()] is Very Very Very Ugly,
68  * but works because program_options copies the string...
69  */
70   generic.add_options()
71     ("help,h", tr("Print help message").toLocal8Bit() )
72     ("version,v", tr("Print version statement").toLocal8Bit() )
73     ;
74 
75   options_description global( tr("Options affecting program behaviour")
76 	.toLocal8Bit().constData() );
77   global.add_options()
78     ("full-page,f", //value<bool>(&m_useFullPage)->default_value(false)->implicit_value(true),
79       tr("Display the full slide on both screens (useful for PDFs created by presentation software other than latex-beamer)").toLocal8Bit() )
80     ("prerender-previous-pages",
81      value<unsigned>(&m_prerenderPreviousPages)->default_value(3),
82      tr("Pre-render the preceding arg slides\n"
83      "NOTE: If you set this to zero, you might not get a thumbnail for the previous slide unless it was loaded already."
84 	 ).toLocal8Bit()
85     )
86     ("prerender-next-pages",
87      value<unsigned>(&m_prerenderNextPages)->default_value(10),
88 	 tr(
89      "Pre-render the next arg slides\n"
90      "NOTE: If you set this to zero, you might not get a thumbnail for the next slide unless it was loaded already."
91 	 ).toLocal8Bit()
92      )
93     ("hyperlink-support,l",
94      value<bool>(&m_hyperlinkSupport)->default_value(true),
95 	 tr(
96      "Support PDF Hyperlinks\n"
97      "Follow hyperlinks when clicked (mouse pointer will change to a pointing hand) - set this to false if "
98      "you cannot reliably control your mouse pointer position and want to always go ahead one slide on click.").toLocal8Bit()
99 	)
100     ("cache-to-memory",
101      value<bool>(&m_cacheToMemory)->default_value(true),
102 	 tr(
103      "Cache the PDF file into memory\n"
104      "Useful if you are editing the PDF file with latex while using the presenter software.").toLocal8Bit()
105      )
106 	("cache-size",
107 		value<unsigned>(&m_cacheSizeMegaBytes)->default_value(1024),
108 		tr(
109 			"Size of the cache for pre-rendered pages, in megabytes."
110 		).toLocal8Bit()
111 	)
112     ("i3-workaround",
113      value<bool>(&m_i3workaround)->default_value(false),
114 	 tr(
115      "Use i3 specific workaround: Execute shellcode once both windows have been created.")
116 #ifndef NDEBUG
117 	 .append( QString::fromUtf8(
118      "\nDebug info: Shellcode is \n"
119      I3WORKAROUND_SHELLCODE
120 	 ) )
121 #endif
122 	 .toLocal8Bit()
123     )
124     ;
125   options_description secondscreen( tr("Options affecting the second screen").toLocal8Bit().constData() );
126   secondscreen.add_options()
127     ("use-second-screen,u",
128      value<bool>(&m_useSecondScreen)->default_value(true),
129 	 tr(
130      "Use the second screen. If you only have one monitor and just want to use this application as a fast, pre-caching PDF viewer"
131      " you might want to say 0 here.\n"
132      "NOTE: Whatever you say on -a, -t, -w, -s or -p doesn't matter if you set this to false.\n"
133      "NOTE: You might want to say -f if you set this to false."
134 	 ).toLocal8Bit()
135     )
136     ("presenter-area,a",
137      value<bool>(&m_showPresenterArea)->default_value(true),
138 	 tr(
139      "Shows or hides the complete \"presenter area\" on the second screen, giving you a full-screen note page.\n"
140      "NOTE: Whatever you say on -t, -w, -s or -p doesnt matter if you set this to false."
141 	 ).toLocal8Bit()
142     )
143     ("duplicate,d",
144      value<bool>(&m_duplicate)->default_value(false),
145      tr("Duplicates the audience's screen next to the notes on the second screen.").toLocal8Bit()
146     )
147     ("thumbnails,t",
148      value<bool>(&m_showThumbnails)->default_value(true),
149 	 tr(
150      "Show thumbnails of previous, current and next slide").toLocal8Bit()
151 	)
152 	("thumbnail-page-part,T",
153 		value<PagePart>(&m_thumbnailPagePart)->default_value(PagePart::FullPage),
154 		tr("Thumbnails show this page part. Valid values are \"left\", \"right\" or \"both\"").toLocal8Bit()
155 	)
156     ("wall-clock,w",
157      value<bool>(&m_showWallClock)->default_value(true),
158 	 tr(
159      "Show the wall clock").toLocal8Bit()
160 	)
161     ("presentation-clock,p",
162      value<bool>(&m_showPresentationClock)->default_value(true),
163      tr(
164 		 "Show the presentation clock").toLocal8Bit()
165 	)
166     ("slide-clock,s",
167      value<bool>(&m_showSlideClock)->default_value(true),
168      tr("Show the slide clock").toLocal8Bit()
169 	)
170     ("bottom-pane-height,b",
171      value<unsigned>(&m_bottomPaneHeightPercent)->default_value(20),
172      tr("Percentage of second screen to use for the bottom pane").toLocal8Bit()
173 	)
174     ;
175 
176   options_description hidden(tr("Hidden options").toLocal8Bit().constData());
177   hidden.add_options()
178     ("pdf-file", value< string >(&m_filePath), tr("PDF File to display").toLocal8Bit())
179     ;
180   positional_options_description p;
181   p.add("pdf-file", 1);
182 
183   options_description help;
184   help.add(generic).add(global).add(secondscreen);
185 
186 
187 
188   options_description commandLineOptions;
189   commandLineOptions.add(help).add(hidden);
190 
191   options_description configFileOptions;
192   configFileOptions.add(global).add(secondscreen);
193 
194   variables_map vm;
195   store( command_line_parser(argc,argv).options(commandLineOptions).positional(p).run(), vm);
196   QString configurationFileLocation = QString::fromUtf8( qgetenv("HOME").append("/.config/dspdfviewer.ini") );
197   {
198     // See if the configuration file exists and is readable
199     std::ifstream cfile( qPrintable(configurationFileLocation) );
200     if ( cfile.good() ) {
201       store( parse_config_file( cfile, configFileOptions), vm);
202     }
203   } // close input file
204   notify(vm);
205 
206   if ( vm.count("version") || vm.count("help") ) {
207     cout << "dspdfviewer version " << DSPDFVIEWER_VERSION << endl;
208 	cout << tr( "Written by Danny Edel\n"
209 			"\n"
210 			"Copyright (C) 2012 Danny Edel.\n"
211 			"This is free software; see the source for copying conditions.  There is NO\n"
212 			"warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.").toLocal8Bit().constData() << endl;
213     if ( vm.count("help")) {
214       cout << endl;
215       cout << tr("Usage: %1 [options] pdf-file").arg( QString::fromUtf8(argv[0]) ).toLocal8Bit().constData() << endl;
216       cout << help << endl;
217       /*: Please try to keep line length below 70 chars and use \t (tab) for padding */
218 		cout << tr("Interactive Controls:\n"
219 			"\tPress F1 or ? during program execution to get a quick\n"
220 			"\toverview about available controls.\n"
221 			"\tPlease read the manpage (man dspdfviewer) for the full list.").toLocal8Bit().constData() << endl;
222     }
223     exit(0);
224   }
225 
226   if ( m_bottomPaneHeightPercent < 1 || m_bottomPaneHeightPercent > 99 ) {
227     throw std::runtime_error( tr("Invalid height in specified. Please use a value from 1 to 99 (inclusive)").toLocal8Bit().constData() );
228   }
229 
230   m_useFullPage = ( 0 < vm.count("full-page") );
231 
232   /** Implied options */
233   if ( ! m_useSecondScreen ) {
234     /* If we dont use a second screen, there's no point in using the presenter area */
235     m_showPresenterArea = false;
236   }
237   if ( ! m_showPresenterArea ) {
238     /* If the presenter area is hidden, disable all clocks and the thumbnails */
239     m_showPresentationClock = false;
240     m_showWallClock = false;
241     m_showSlideClock = false;
242     /* This option will effectively disable rendering of thumbnails */
243     m_showThumbnails = false;
244   }
245 }
246 
filePath() const247 string RuntimeConfiguration::filePath() const
248 {
249   if ( m_filePath.empty() ) {
250     throw noFileNameException();
251   }
252 
253   return m_filePath;
254 }
255 
filePathQString() const256 QString RuntimeConfiguration::filePathQString() const
257 {
258   return QString::fromLocal8Bit( filePath().c_str() );
259 }
260 
useFullPage() const261 bool RuntimeConfiguration::useFullPage() const
262 {
263   return m_useFullPage;
264 }
265 
showPresentationClock() const266 bool RuntimeConfiguration::showPresentationClock() const
267 {
268   return m_showPresentationClock;
269 }
showPresenterArea() const270 bool RuntimeConfiguration::showPresenterArea() const
271 {
272   return m_showPresenterArea;
273 }
duplicate() const274 bool RuntimeConfiguration::duplicate() const
275 {
276   return m_duplicate;
277 }
showSlideClock() const278 bool RuntimeConfiguration::showSlideClock() const
279 {
280   return m_showSlideClock;
281 }
showThumbnails() const282 bool RuntimeConfiguration::showThumbnails() const
283 {
284   return m_showThumbnails;
285 }
showWallClock() const286 bool RuntimeConfiguration::showWallClock() const
287 {
288   return m_showWallClock;
289 }
290 
prerenderNextPages() const291 unsigned int RuntimeConfiguration::prerenderNextPages() const
292 {
293   return m_prerenderNextPages;
294 }
prerenderPreviousPages() const295 unsigned int RuntimeConfiguration::prerenderPreviousPages() const
296 {
297   return m_prerenderPreviousPages;
298 }
299 
useSecondScreen() const300 bool RuntimeConfiguration::useSecondScreen() const
301 {
302   return m_useSecondScreen;
303 }
304 
cacheSetting() const305 PDFCacheOption RuntimeConfiguration::cacheSetting() const
306 {
307   return m_cacheToMemory?
308 	PDFCacheOption::keepPDFinMemory :
309 	PDFCacheOption::rereadFromDisk;
310 }
311 
bottomPaneHeight() const312 unsigned int RuntimeConfiguration::bottomPaneHeight() const
313 {
314   return m_bottomPaneHeightPercent;
315 }
316 
hyperlinkSupport() const317 bool RuntimeConfiguration::hyperlinkSupport() const
318 {
319   return m_hyperlinkSupport;
320 }
321 
filePath(const std::string & newPath)322 void RuntimeConfiguration::filePath(const std::string& newPath )
323 {
324 	m_filePath = newPath;
325 }
326 
filePathDefined() const327 bool RuntimeConfiguration::filePathDefined() const
328 {
329 	return ! m_filePath.empty();
330 }
331 
noFileNameException()332 noFileNameException::noFileNameException():
333 	logic_error( QCoreApplication::translate("DSPDFViewer", "You did not specify a PDF-File to display.").toLocal8Bit().constData() ) {
334 }
335 
i3workaround() const336 bool RuntimeConfiguration::i3workaround() const
337 {
338 	return m_i3workaround;
339 }
340 
i3workaround_shellcode() const341 std::string RuntimeConfiguration::i3workaround_shellcode() const
342 {
343 	return std::string( I3WORKAROUND_SHELLCODE );
344 }
345 
thumbnailPagePart() const346 PagePart RuntimeConfiguration::thumbnailPagePart() const
347 {
348 	return m_thumbnailPagePart;
349 }
350 
cacheSizeBytes() const351 unsigned RuntimeConfiguration::cacheSizeBytes() const {
352 	return cacheSizeMegaBytes()*1024*1024;
353 }
354 
cacheSizeMegaBytes() const355 unsigned RuntimeConfiguration::cacheSizeMegaBytes() const{
356 	return m_cacheSizeMegaBytes;
357 }
358