]> git.lyx.org Git - lyx.git/blob - src/frontends/gtk/lyx_gui.C
Display accelerator (binding) labels in menus.
[lyx.git] / src / frontends / gtk / lyx_gui.C
1 /**
2  * \file gtk/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 Lars Gullik Bjnes
7  * \author John Levon
8  * \author Huang Ying
9  *
10  * Full author contact details are available in file CREDITS.
11  */
12
13 #include <config.h>
14
15 #include "lyx_gui.h"
16
17 #include "debug.h"
18 #include "funcrequest.h"
19 #include "gettext.h"
20
21 #include "LColor.h"
22 #include "LyXAction.h"
23 #include "lyx_main.h"
24 #include "lyxrc.h"
25 #include "lyxfont.h"
26 #include "graphics/LoaderQueue.h"
27
28 #include "io_callback.h"
29
30 // FIXME: move this stuff out again
31 #include "bufferlist.h"
32 #include "buffer_funcs.h"
33 #include "lyxfunc.h"
34 #include "lyxserver.h"
35 #include "lyxsocket.h"
36 #include "BufferView.h"
37
38 #include "GView.h"
39 #include "GtkmmX.h"
40
41 #include "xftFontLoader.h"
42 #include "GWorkArea.h"
43
44 //just for xforms
45 #include "lyx_forms.h"
46 #include "xformsImage.h"
47 #include "xforms_helpers.h"
48
49 #include "support/lyxlib.h"
50 #include "support/os.h"
51 #include "support/filetools.h"
52 #include "support/path_defines.h"
53
54 #include <gtkmm.h>
55
56 #include <boost/bind.hpp>
57 #include <boost/function.hpp>
58 #include <boost/shared_ptr.hpp>
59
60 #include <fcntl.h>
61
62 #include <sstream>
63 #include <iomanip>
64
65 namespace os = lyx::support::os;
66
67 using std::ostringstream;
68 using std::string;
69
70 using lyx::frontend::colorCache;
71 using lyx::frontend::GView;
72 using lyx::frontend::XformsColor;
73
74
75 extern BufferList bufferlist;
76
77 // FIXME: wrong place !
78 LyXServer * lyxserver;
79 LyXServerSocket * lyxsocket;
80
81 bool lyx_gui::use_gui = true;
82
83 namespace {
84
85 /// quit lyx
86 bool finished = false;
87
88
89 /// estimate DPI from X server
90 float getDPI()
91 {
92         Screen * scr = ScreenOfDisplay(getDisplay(), getScreen());
93         return ((HeightOfScreen(scr) * 25.4 / HeightMMOfScreen(scr)) +
94                 (WidthOfScreen(scr) * 25.4 / WidthMMOfScreen(scr))) / 2;
95 }
96
97
98 /// set default GUI configuration
99 void setDefaults()
100 {
101         FL_IOPT cntl;
102         cntl.buttonFontSize = FL_NORMAL_SIZE;
103         cntl.browserFontSize = FL_NORMAL_SIZE;
104         cntl.labelFontSize = FL_NORMAL_SIZE;
105         cntl.choiceFontSize = FL_NORMAL_SIZE;
106         cntl.inputFontSize = FL_NORMAL_SIZE;
107         cntl.menuFontSize  = FL_NORMAL_SIZE;
108         cntl.borderWidth = -1;
109         cntl.vclass = FL_DefaultVisual;
110         fl_set_defaults(FL_PDVisual
111                         | FL_PDButtonFontSize
112                         | FL_PDBrowserFontSize
113                         | FL_PDLabelFontSize
114                         | FL_PDChoiceFontSize
115                         | FL_PDInputFontSize
116                         | FL_PDMenuFontSize
117                         | FL_PDBorderWidth, &cntl);
118 }
119
120
121 extern "C" {
122
123
124 int LyX_XErrHandler(Display * display, XErrorEvent * xeev) {
125         // We don't abort on BadWindow
126         if (xeev->error_code == BadWindow) {
127                 lyxerr << "BadWindow received !" << std::endl;
128                 lyxerr << "If you're using xforms 1.0 or greater, "
129                        << " please report this to lyx-devel@lists.lyx.org"
130                        << std::endl;
131                 return 0;
132         }
133
134         // emergency cleanup
135         LyX::cref().emergencyCleanup();
136
137         // Get the reason for the crash.
138         char etxt[513];
139         XGetErrorText(display, xeev->error_code, etxt, 512);
140         lyxerr << etxt << " id: " << xeev->resourceid << std::endl;
141         // By doing an abort we get a nice backtrace. (hopefully)
142         lyx::support::abort();
143         return 0;
144 }
145
146 }
147
148 /// read in geometry specification
149 char geometry[40];
150
151 } // namespace anon
152
153
154 void parse_init_xforms(int & argc, char * argv[])
155 {
156         setDefaults();
157
158         FL_CMD_OPT cmdopt[] = {
159                 {"-geometry", "*.geometry", XrmoptionSepArg, "690x510"}
160         };
161
162         FL_resource res[] = {
163                 {"geometry", "geometryClass", FL_STRING, geometry, "", 40}
164         };
165
166         const int num_res = sizeof(res)/sizeof(FL_resource);
167         fl_initialize(&argc, argv, "LyX", cmdopt, num_res);
168
169         // It appears that, in xforms >=0.89.5, fl_initialize()
170         // calls setlocale() and ruins our LC_NUMERIC setting.
171
172         fl_get_app_resources(res, num_res);
173
174         Display * display = fl_get_display();
175
176         if (!display) {
177                 lyxerr << "LyX: unable to access X display, exiting"
178                        << std::endl;
179                 lyx::support::os::warn("Unable to access X display, exiting");
180                 ::exit(1);
181         }
182
183         fcntl(ConnectionNumber(display), F_SETFD, FD_CLOEXEC);
184
185         XSetErrorHandler(LyX_XErrHandler);
186
187         using namespace lyx::graphics;
188
189         // connect the image loader based on the xforms library
190         Image::newImage = boost::bind(&xformsImage::newImage);
191         Image::loadableFormats = boost::bind(&xformsImage::loadableFormats);
192 }
193
194
195 void lyx_gui::parse_init(int & argc, char * argv[])
196 {
197         new Gtk::Main(argc, argv);
198
199         parse_init_xforms(argc, argv);
200
201         locale_init();
202
203         // must do this /before/ lyxrc gets read
204         lyxrc.dpi = getDPI();
205 }
206
207
208 void parse_lyxrc_xforms()
209 {
210         XformsColor::read(lyx::support::AddName(
211                                   lyx::support::user_lyxdir(), "preferences.xform"));
212
213         if (lyxrc.popup_font_encoding.empty())
214                 lyxrc.popup_font_encoding = lyxrc.font_norm;
215         // Set the font name for popups and menus
216         string boldfontname = lyxrc.popup_bold_font
217                                + "-*-*-*-?-*-*-*-*-"
218                                + lyxrc.popup_font_encoding;
219                 // "?" means "scale that font"
220         string fontname = lyxrc.popup_normal_font
221                                + "-*-*-*-?-*-*-*-*-"
222                                + lyxrc.popup_font_encoding;
223
224         int bold = fl_set_font_name(FL_BOLD_STYLE, boldfontname.c_str());
225         int normal = fl_set_font_name(FL_NORMAL_STYLE, fontname.c_str());
226         if (bold < 0)
227                 lyxerr << "Could not set menu font to "
228                        << boldfontname << std::endl;
229
230         if (normal < 0)
231                 lyxerr << "Could not set popup font to "
232                        << fontname << std::endl;
233
234         if (bold < 0 && normal < 0) {
235                 lyxerr << "Using 'helvetica' font for menus" << std::endl;
236                 boldfontname = "-*-helvetica-bold-r-*-*-*-?-*-*-*-*-iso8859-1";
237                 fontname = "-*-helvetica-medium-r-*-*-*-?-*-*-*-*-iso8859-1";
238                 bold = fl_set_font_name(FL_BOLD_STYLE, boldfontname.c_str());
239                 normal = fl_set_font_name(FL_NORMAL_STYLE, fontname.c_str());
240
241                 if (bold < 0 && normal < 0) {
242                         lyxerr << "Could not find helvetica font. Using 'fixed'."
243                                << std::endl;
244                         fl_set_font_name(FL_NORMAL_STYLE, "fixed");
245                         normal = bold = 0;
246                 }
247         }
248         if (bold < 0)
249                 fl_set_font_name(FL_BOLD_STYLE, fontname.c_str());
250         else if (normal < 0)
251                 fl_set_font_name(FL_NORMAL_STYLE, boldfontname.c_str());
252
253         fl_setpup_fontstyle(FL_NORMAL_STYLE);
254         fl_setpup_fontsize(FL_NORMAL_SIZE);
255         fl_setpup_color(FL_MCOL, FL_BLACK);
256         fl_set_goodies_font(FL_NORMAL_STYLE, FL_NORMAL_SIZE);
257         fl_set_tooltip_font(FL_NORMAL_STYLE, FL_NORMAL_SIZE);
258 }
259
260
261 void lyx_gui::parse_lyxrc()
262 {
263         parse_lyxrc_xforms();
264 }
265
266
267 void start_xforms()
268 {
269         // initial geometry
270         int xpos = -1;
271         int ypos = -1;
272         unsigned int width = 690;
273         unsigned int height = 510;
274
275         int const geometryBitmask =
276                 XParseGeometry(geometry,
277                                &xpos, &ypos, &width, &height);
278
279         // if width is not set by geometry, check it against monitor width
280         if (!(geometryBitmask & WidthValue)) {
281                 Screen * scr = ScreenOfDisplay(fl_get_display(), fl_screen);
282                 if (WidthOfScreen(scr) - 8 < int(width))
283                         width = WidthOfScreen(scr) - 8;
284         }
285
286         // if height is not set by geometry, check it against monitor height
287         if (!(geometryBitmask & HeightValue)) {
288                 Screen * scr = ScreenOfDisplay(fl_get_display(), fl_screen);
289                 if (HeightOfScreen(scr) - 24 < int(height))
290                         height = HeightOfScreen(scr) - 24;
291         }
292
293         Screen * s = ScreenOfDisplay(fl_get_display(), fl_screen);
294
295         // recalculate xpos if it's not set
296         if (xpos == -1)
297                 xpos = (WidthOfScreen(s) - width) / 2;
298
299         // recalculate ypos if it's not set
300         if (ypos == -1)
301                 ypos = (HeightOfScreen(s) - height) / 2;
302
303         lyxerr[Debug::GUI] << "Creating view: " << width << 'x' << height
304                            << '+' << xpos << '+' << ypos << std::endl;
305
306 //      XFormsView view(width, height);
307 //      view.show(xpos, ypos, "LyX");
308 //      view.init();
309 }
310
311
312 static void events_xforms()
313 {
314         if (fl_check_forms() == FL_EVENT) {
315                 XEvent ev;
316                 fl_XNextEvent(&ev);
317                 lyxerr[Debug::GUI]
318                         << "Received unhandled X11 event" << std::endl
319                         << "Type: " << ev.xany.type
320                         << " Target: 0x" << std::hex << ev.xany.window
321                         << std::dec << std::endl;
322         }
323 }
324
325
326 void lyx_gui::start(string const & batch, std::vector<string> const & files)
327 {
328         start_xforms();
329         // just for debug
330         XSynchronize(getDisplay(), true);
331
332         boost::shared_ptr<GView> view_ptr(new GView);
333         LyX::ref().addLyXView(view_ptr);
334
335         GView & view = *view_ptr.get();
336         view.show();
337         view.init();
338
339         // FIXME: server code below needs moving
340
341         lyxserver = new LyXServer(&view.getLyXFunc(), lyxrc.lyxpipes);
342         lyxsocket = new LyXServerSocket(&view.getLyXFunc(),
343                           os::slashify_path(os::getTmpDir() + "/lyxsocket"));
344
345         for_each(files.begin(), files.end(),
346                  bind(&BufferView::loadLyXFile, view.view(), _1, true));
347
348         // handle the batch commands the user asked for
349         if (!batch.empty()) {
350                 view.getLyXFunc().dispatch(lyxaction.lookupFunc(batch));
351         }
352
353         // enter the event loop
354         while (!finished) {
355                 while (Gtk::Main::events_pending())
356                         Gtk::Main::iteration(false);
357                 events_xforms();
358         }
359
360         // FIXME: breaks emergencyCleanup
361         delete lyxsocket;
362         delete lyxserver;
363 }
364
365
366 void lyx_gui::exit()
367 {
368         finished = true;
369 }
370
371
372 FuncStatus lyx_gui::getStatus(FuncRequest const & /*ev*/)
373 {
374         // Nothing interesting to do here
375         return FuncStatus();
376 }
377
378
379 string const lyx_gui::hexname(LColor_color col)
380 {
381         Gdk::Color gdkColor;
382         Gdk::Color * gclr = colorCache.getColor(col);
383         if (!gclr) {
384                 gclr = &gdkColor;
385                 gclr->parse(lcolor.getX11Name(col));
386         }
387
388         std::ostringstream os;
389
390         // Note that X stores the RGB values in the range 0 - 65535
391         // whilst we require them in the range 0 - 255.
392         os << std::setbase(16) << std::setfill('0')
393            << std::setw(2) << (gclr->get_red() / 256)
394            << std::setw(2) << (gclr->get_green() / 256)
395            << std::setw(2) << (gclr->get_blue() / 256);
396
397         return os.str();
398 }
399
400
401 void lyx_gui::update_color(LColor_color /*col*/)
402 {
403         colorCache.clear();
404 }
405
406
407 void lyx_gui::update_fonts()
408 {
409         fontLoader.update();
410 }
411
412
413 bool lyx_gui::font_available(LyXFont const & font)
414 {
415         return fontLoader.available(font);
416 }
417
418
419 namespace {
420
421 std::map<int, boost::shared_ptr<io_callback> > callbacks;
422
423 } // NS anon
424
425
426 void lyx_gui::register_socket_callback(int fd,
427                                        boost::function<void()> func)
428 {
429         callbacks[fd] = boost::shared_ptr<io_callback>(new io_callback(fd, func));
430 }
431
432
433 void lyx_gui::unregister_socket_callback(int fd)
434 {
435         callbacks.erase(fd);
436 }
437
438
439 string const lyx_gui::roman_font_name()
440 {
441         return "times";
442 }
443
444
445 string const lyx_gui::sans_font_name()
446 {
447         return "helvetica";
448 }
449
450
451 string const lyx_gui::typewriter_font_name()
452 {
453         return "courier";
454 }
455
456
457 void lyx_gui::sync_events()
458 {
459         // FIXME
460 }