]> git.lyx.org Git - lyx.git/blob - src/frontends/qt4/lyx_gui.C
Fix hang on exit under Windows.
[lyx.git] / src / frontends / qt4 / lyx_gui.C
1 /**
2  * \file qt4/lyx_gui.C
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author unknown
7  * \author John Levon
8  *
9  * Full author contact details are available in file CREDITS.
10  */
11
12 #include <config.h>
13
14 #include "lyx_gui.h"
15
16 // FIXME: move this stuff out again
17 #include "bufferlist.h"
18 #include "BufferView.h"
19 #include "Color.h"
20 #include "funcrequest.h"
21 #include "LColor.h"
22 #include "lyx_main.h"
23 #include "LyXAction.h"
24 #include "lyxfunc.h"
25 #include "lyxrc.h"
26 #include "lyxserver.h"
27 #include "lyxsocket.h"
28 #include "session.h"
29
30 #include "graphics/LoaderQueue.h"
31
32 #include "support/lstrings.h"
33 #include "support/convert.h"
34 #include "support/os.h"
35 #include "support/package.h"
36 #include "debug.h"
37
38 // Dear Lord, deliver us from Evil, aka the Qt headers
39 // Qt defines a macro 'signals' that clashes with a boost namespace.
40 // All is well if the namespace is visible first.
41 #include <boost/signal.hpp> // FIXME: Is this needed? (Lgb)
42 #include <boost/bind.hpp>
43 #include <boost/shared_ptr.hpp>
44
45 #include "QtView.h"
46 #include "lcolorcache.h"
47 #include "qfont_loader.h"
48 #include "QLImage.h"
49 #include "qt_helpers.h"
50 #include "socket_callback.h"
51
52 #ifdef Q_WS_MACX
53 #include <Carbon/Carbon.h>
54 #endif
55
56 #include <QApplication>
57 #include <QEventLoop>
58 #include <QTranslator>
59 #include <QTextCodec>
60
61 using lyx::support::ltrim;
62 using lyx::support::package;
63
64 using lyx::frontend::QtView;
65
66 namespace os = lyx::support::os;
67
68 using boost::shared_ptr;
69
70 #ifndef CXX_GLOBAL_CSTD
71 using std::exit;
72 #endif
73
74 using std::map;
75 using std::vector;
76 using std::string;
77
78
79 extern BufferList bufferlist;
80
81 namespace {
82
83 int getDPI()
84 {
85         QWidget w;
86         return int(0.5 * (w.logicalDpiX() + w.logicalDpiY()));
87 }
88
89 map<int, shared_ptr<socket_callback> > socket_callbacks;
90
91 } // namespace anon
92
93 // FIXME: wrong place !
94 LyXServer * lyxserver;
95 LyXServerSocket * lyxsocket;
96
97 // in QLyXKeySym.C
98 extern void initEncodings();
99
100 #ifdef Q_WS_X11
101 extern bool lyxX11EventFilter(XEvent * xev);
102 #endif
103
104 #ifdef Q_WS_MACX
105 extern bool macEventFilter(EventRef event);
106 extern pascal OSErr
107 handleOpenDocuments(const AppleEvent* inEvent, AppleEvent* /*reply*/,
108                     long /*refCon*/);
109 #endif
110
111 class LQApplication : public QApplication
112 {
113 public:
114         LQApplication(int & argc, char ** argv);
115         ~LQApplication();
116 #ifdef Q_WS_X11
117         bool x11EventFilter (XEvent * ev) { return lyxX11EventFilter(ev); }
118 #endif
119 #ifdef Q_WS_MACX
120         bool macEventFilter(EventRef event);
121 #endif
122 };
123
124
125 LQApplication::LQApplication(int & argc, char ** argv)
126         : QApplication(argc, argv)
127 {
128 #ifdef Q_WS_MACX
129         AEInstallEventHandler(kCoreEventClass, kAEOpenDocuments,
130                               NewAEEventHandlerUPP(handleOpenDocuments),
131                               0, false);
132 #endif
133 }
134
135
136 LQApplication::~LQApplication()
137 {}
138
139
140 #ifdef Q_WS_MACX
141 bool LQApplication::macEventFilter(EventRef event)
142 {
143         if (GetEventClass(event) == kEventClassAppleEvent) {
144                 EventRecord eventrec;
145                 ConvertEventRefToEventRecord(event, &eventrec);
146                 AEProcessAppleEvent(&eventrec);
147
148                 return false;
149         }
150         return false;
151 }
152 #endif
153
154
155 namespace {
156
157 LQApplication * app = 0;
158
159 }
160
161
162 namespace lyx_gui {
163
164 bool use_gui = true;
165
166 void parse_init(int & argc, char * argv[])
167 {
168         // Force adding of font path _before_ QApplication is initialized
169         FontLoader::initFontPath();
170
171
172 #ifdef Q_WS_WIN
173         static QApplication win_app(argc, argv);
174 #else
175         app = new LQApplication(argc, argv);
176 #endif
177
178         // install translation file for Qt built-in dialogs
179         // These are only installed since Qt 3.2.x
180         static QTranslator qt_trans(0);
181         if (qt_trans.load(QString("qt_") + QTextCodec::locale(),
182                           qInstallPathTranslations())) {
183                 qApp->installTranslator(&qt_trans);
184                 // even if the language calls for RtL, don't do that
185                 qApp->setReverseLayout(false);
186                 lyxerr[Debug::GUI]
187                         << "Successfully installed Qt translations for locale "
188                         << QTextCodec::locale() << std::endl;
189         } else
190                 lyxerr[Debug::GUI]
191                         << "Could not find  Qt translations for locale "
192                         << QTextCodec::locale() << std::endl;
193
194 /*#ifdef Q_WS_MACX
195         // These translations are meant to break Qt/Mac menu merging
196         // algorithm on some entries. It lists the menu names that
197         // should not be moved to the LyX menu
198         static QTranslator aqua_trans(0);
199         aqua_trans.insert(QTranslatorMessage("QMenuBar", "Setting", 0,
200                                              "do_not_merge_me"));
201         aqua_trans.insert(QTranslatorMessage("QMenuBar", "Config", 0,
202                                              "do_not_merge_me"));
203         aqua_trans.insert(QTranslatorMessage("QMenuBar", "Options", 0,
204                                              "do_not_merge_me"));
205         aqua_trans.insert(QTranslatorMessage("QMenuBar", "Setup", 0,
206                                              "do_not_merge_me"));
207
208         app.installTranslator(&aqua_trans);
209 #endif
210 */
211         using namespace lyx::graphics;
212
213         Image::newImage = boost::bind(&QLImage::newImage);
214         Image::loadableFormats = boost::bind(&QLImage::loadableFormats);
215
216         // needs to be done before reading lyxrc
217         lyxrc.dpi = getDPI();
218
219         LoaderQueue::setPriority(10,100);
220 }
221
222
223 void parse_lyxrc()
224 {}
225
226
227 void start(string const & batch, vector<string> const & files)
228 {
229         // this can't be done before because it needs the Languages object
230         initEncodings();
231
232         // initial geometry
233         unsigned int width = 690;
234         unsigned int height = 510;
235         // first try lyxrc
236         if (lyxrc.geometry_width != 0 && lyxrc.geometry_height != 0 ) {
237                 width = lyxrc.geometry_width;
238                 height = lyxrc.geometry_height;
239         }
240         // if lyxrc returns (0,0), then use session info
241         else {
242                 string val = LyX::ref().session().loadSessionInfo("WindowWidth");
243                 if (val != "")
244                         width = convert<unsigned int>(val);
245                 val = LyX::ref().session().loadSessionInfo("WindowHeight");
246                 if (val != "")
247                         height = convert<unsigned int>(val);
248         }
249
250         boost::shared_ptr<QtView> view_ptr(new QtView(width, height));
251         LyX::ref().addLyXView(view_ptr);
252
253         QtView & view = *view_ptr.get();
254
255         // if user wants to restore window position
256         if (lyxrc.geometry_xysaved) {
257                 QPoint p = view.pos();
258                 string val = LyX::ref().session().loadSessionInfo("WindowPosX");
259                 if (val != "")
260                         p.setX(convert<unsigned int>(val));
261                 val = LyX::ref().session().loadSessionInfo("WindowPosY");
262                 if (val != "")
263                         p.setY(convert<unsigned int>(val));
264                 view.move(p);
265         }
266         view.show();
267         view.init();
268
269         // FIXME: some code below needs moving
270
271         lyxserver = new LyXServer(&view.getLyXFunc(), lyxrc.lyxpipes);
272         lyxsocket = new LyXServerSocket(&view.getLyXFunc(),
273                           os::internal_path(package().temp_dir() + "/lyxsocket"));
274
275         for_each(files.begin(), files.end(),
276                  bind(&BufferView::loadLyXFile, view.view(), _1, true));
277
278         // handle the batch commands the user asked for
279         if (!batch.empty()) {
280                 view.getLyXFunc().dispatch(lyxaction.lookupFunc(batch));
281         }
282
283         qApp->exec();
284
285         // FIXME
286         delete lyxsocket;
287         delete lyxserver;
288         lyxserver = 0;
289         delete app;
290 }
291
292
293 void sync_events()
294 {
295         // This is the ONLY place where processEvents may be called.
296         // During screen update/ redraw, this method is disabled to
297         // prevent keyboard events being handed to the LyX core, where
298         // they could cause re-entrant calls to screen update.
299 #if QT_VERSION >= 0x030100
300         qApp->processEvents(QEventLoop::ExcludeUserInput);
301 #endif
302 }
303
304
305 void exit()
306 {
307         delete lyxsocket;
308         delete lyxserver;
309         lyxserver = 0;
310
311         // we cannot call qApp->exit(0) - that could return us
312         // into a static dialog return in the lyx code (for example,
313         // load autosave file QMessageBox. We have to just get the hell
314         // out.
315
316         ::exit(0);
317 }
318
319
320 FuncStatus getStatus(FuncRequest const & ev)
321 {
322         FuncStatus flag;
323         switch (ev.action) {
324         case LFUN_DIALOG_SHOW:
325                 if (ev.argument == "preamble")
326                         flag.unknown(true);
327                 break;
328         case LFUN_TOOLTIPS_TOGGLE:
329                 flag.unknown(true);
330                 break;
331         default:
332                 break;
333         }
334
335         return flag;
336 }
337
338
339 bool getRGBColor(LColor_color col, lyx::RGBColor & rgbcol)
340 {
341         QColor const & qcol = lcolorcache.get(col);
342         if (!qcol.isValid()) {
343                 rgbcol.r = 0;
344                 rgbcol.g = 0;
345                 rgbcol.b = 0;
346                 return false;
347         }
348         rgbcol.r = qcol.red();
349         rgbcol.g = qcol.green();
350         rgbcol.b = qcol.blue();
351         return true;
352 }
353
354
355 string const hexname(LColor_color col)
356 {
357         return ltrim(fromqstr(lcolorcache.get(col).name()), "#");
358 }
359
360
361 void update_color(LColor_color)
362 {
363         // FIXME: Bleh, can't we just clear them all at once ?
364         lcolorcache.clear();
365 }
366
367
368 void update_fonts()
369 {
370         fontloader.update();
371 }
372
373
374 bool font_available(LyXFont const & font)
375 {
376         return fontloader.available(font);
377 }
378
379
380 void register_socket_callback(int fd, boost::function<void()> func)
381 {
382         socket_callbacks[fd] = shared_ptr<socket_callback>(new socket_callback(fd, func));
383 }
384
385
386 void unregister_socket_callback(int fd)
387 {
388         socket_callbacks.erase(fd);
389 }
390
391
392 string const roman_font_name()
393 {
394         if (!use_gui)
395                 return "serif";
396
397         QFont font;
398         font.setStyleHint(QFont::Serif);
399         font.setFamily("serif");
400
401         return fromqstr(QFontInfo(font).family());
402 }
403
404
405 string const sans_font_name()
406 {
407         if (!use_gui)
408                 return "sans";
409
410         QFont font;
411         font.setStyleHint(QFont::SansSerif);
412         font.setFamily("sans");
413
414         return fromqstr(QFontInfo(font).family());
415 }
416
417
418 string const typewriter_font_name()
419 {
420         if (!use_gui)
421                 return "monospace";
422
423         QFont font;
424         font.setStyleHint(QFont::TypeWriter);
425         font.setFamily("monospace");
426
427         return fromqstr(QFontInfo(font).family());
428 }
429
430 }; // namespace lyx_gui