]> git.lyx.org Git - lyx.git/blob - src/frontends/screen.C
b0b3efed918c7ec035e43bcc7b90f69c9fee6bb7
[lyx.git] / src / frontends / screen.C
1 /**
2  * \file screen.C
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author John Levon
7  *
8  * Full author contact details are available in file CREDITS
9  *
10  * Splash screen code added by Angus Leeming
11  */
12
13
14 #include <config.h>
15
16 #include "screen.h"
17 #include "lyxtext.h"
18 #include "lyxrc.h"
19 #include "lyxrow.h"
20 #include "BufferView.h"
21 #include "buffer.h"
22 #include "WorkArea.h"
23 #include "Painter.h"
24 #include "font_metrics.h"
25 #include "language.h"
26 #include "debug.h"
27
28 // Splash screen-specific stuff
29 #include "lyxfont.h"
30 #include "version.h"
31
32 #include "graphics/GraphicsLoader.h"
33 #include "graphics/GraphicsImage.h"
34
35 #include "support/filetools.h" // LibFileSearch
36
37 #include <boost/utility.hpp>
38 #include <boost/bind.hpp>
39 #include <boost/signals/trackable.hpp>
40
41 using std::min;
42 using std::max;
43 using std::endl;
44
45 namespace {
46
47 class SplashScreen : boost::noncopyable, boost::signals::trackable {
48 public:
49         /// This is a singleton class. Get the instance.
50         static SplashScreen const & get();
51         ///
52         grfx::Image const * image() const { return loader_.image(); }
53         ///
54         string const & text() const { return text_; }
55         ///
56         LyXFont const & font() const { return font_; }
57         ///
58         void connect(grfx::Loader::slot_type const & slot) const {
59                 loader_.connect(slot);
60         }
61         ///
62         void startLoading() const {
63                 if (loader_.status() == grfx::WaitingToLoad)
64                         loader_.startLoading();
65         }
66
67 private:
68         /** Make the c-tor private so we can control how many objects
69          *  are instantiated.
70          */
71         SplashScreen();
72
73         ///
74         grfx::Loader loader_;
75         /// The text to be written on top of the pixmap
76         string const text_;
77         /// in this font...
78         LyXFont font_;
79 };
80
81
82 SplashScreen const & SplashScreen::get()
83 {
84         static SplashScreen singleton;
85         return singleton;
86 }
87
88
89 SplashScreen::SplashScreen()
90         : text_(lyx_version ? lyx_version : "unknown")
91 {
92         if (!lyxrc.show_banner)
93                 return;
94
95         string const file = LibFileSearch("images", "banner", "ppm");
96         if (file.empty())
97                 return;
98
99         // The font used to display the version info
100         font_.setFamily(LyXFont::SANS_FAMILY);
101         font_.setSeries(LyXFont::BOLD_SERIES);
102         font_.setSize(LyXFont::SIZE_NORMAL);
103         font_.setColor(LColor::yellow);
104
105         // Load up the graphics file
106         loader_.reset(file);
107 }
108
109 } // namespace anon
110
111
112 LyXScreen::LyXScreen()
113         : cursor_visible_(false), force_clear_(true), greyed_out_(false)
114 {
115         // Start loading the pixmap as soon as possible
116         if (lyxrc.show_banner) {
117                 SplashScreen const & splash = SplashScreen::get();
118                 splash.connect(boost::bind(&LyXScreen::greyOut, this));
119                 splash.startLoading();
120         }
121 }
122
123
124 LyXScreen::~LyXScreen()
125 {
126 }
127
128 // FIXME: GUII these cursor methods need to decide
129 // whether the workarea is focused or not
130
131 void LyXScreen::showCursor(LyXText const * text, BufferView const * bv)
132 {
133         if (cursor_visible_)
134                 return;
135
136         workarea().getPainter().start();
137
138         Cursor_Shape shape = BAR_SHAPE;
139         BufferParams const & bp(bv->buffer()->params);
140         LyXFont const & realfont(text->real_current_font);
141
142         if (realfont.language() != bp.language
143                 || realfont.isVisibleRightToLeft()
144                 != bp.language->RightToLeft()) {
145                 shape = (realfont.isVisibleRightToLeft())
146                         ? REVERSED_L_SHAPE : L_SHAPE;
147         }
148
149         showManualCursor(text, text->cursor.x(), text->cursor.y(),
150                 font_metrics::maxAscent(realfont),
151                 font_metrics::maxDescent(realfont),
152                 shape);
153
154         workarea().getPainter().end();
155 }
156
157
158 bool LyXScreen::fitManualCursor(BufferView * bv, LyXText * text,
159         int /*x*/, int y, int asc, int desc)
160 {
161         int const vheight = workarea().workHeight();
162         int newtop = text->first_y;
163
164         if (y + desc - text->first_y >= vheight)
165                 newtop = y - 3 * vheight / 4;  // the scroll region must be so big!!
166         else if (y - asc < text->first_y
167                 && text->first_y > 0) {
168                 newtop = y - vheight / 4;
169         }
170
171         newtop = max(newtop, 0); // can newtop ever be < 0? (Lgb)
172
173         if (newtop != text->first_y) {
174                 draw(text, bv, newtop);
175                 text->first_y = newtop;
176                 return true;
177         }
178
179         return false;
180 }
181
182
183 void LyXScreen::cursorToggle(BufferView * bv) const
184 {
185         if (cursor_visible_)
186                 bv->hideCursor();
187         else
188                 bv->showCursor();
189 }
190
191
192 unsigned int LyXScreen::topCursorVisible(LyXCursor const & cursor, int top_y)
193 {
194         int const vheight = workarea().workHeight();
195         int newtop = top_y;
196
197         Row * row = cursor.row();
198
199         // Is this a hack? Yes, probably... (Lgb)
200         if (!row)
201                 return max(newtop, 0);
202
203         if (cursor.y() - row->baseline() + row->height() - top_y >= vheight) {
204                 if (row->height() < vheight
205                     && row->height() > vheight / 4) {
206                         newtop = cursor.y()
207                                 + row->height()
208                                 - row->baseline() - vheight;
209                 } else {
210                         // scroll down
211                         newtop = cursor.y()
212                                 - vheight / 2;   /* the scroll region must be so big!! */
213                 }
214
215         } else if (static_cast<int>((cursor.y()) - row->baseline()) <
216                    top_y && top_y > 0) {
217                 if (row->height() < vheight
218                     && row->height() > vheight / 4) {
219                         newtop = cursor.y() - row->baseline();
220                 } else {
221                         // scroll up
222                         newtop = cursor.y() - vheight / 2;
223                         newtop = min(newtop, top_y);
224                 }
225         }
226
227         newtop = max(newtop, 0);
228
229         return newtop;
230 }
231
232
233 bool LyXScreen::fitCursor(LyXText * text, BufferView * bv)
234 {
235         // Is a change necessary?
236         int const newtop = topCursorVisible(text->cursor, text->first_y);
237         bool const result = (newtop != text->first_y);
238         if (result) {
239                 draw(text, bv, newtop);
240         }
241
242         return result;
243 }
244
245
246 void LyXScreen::update(LyXText * text, BufferView * bv,
247         int yo, int xo)
248 {
249         int const vwidth = workarea().workWidth();
250         int const vheight = workarea().workHeight();
251
252         workarea().getPainter().start();
253
254         switch (text->status()) {
255         case LyXText::NEED_MORE_REFRESH:
256         {
257                 int const y = max(int(text->refresh_y - text->first_y), 0);
258                 drawFromTo(text, bv, y, vheight, yo, xo);
259                 text->refresh_y = 0;
260                 // otherwise this is called ONLY from BufferView_pimpl(update)
261                 // or we should see to set this flag accordingly
262                 if (text != bv->text)
263                         text->status(bv, LyXText::UNCHANGED);
264                 expose(0, y, vwidth, vheight - y);
265         }
266         break;
267         case LyXText::NEED_VERY_LITTLE_REFRESH:
268         {
269                 // ok I will update the current cursor row
270                 drawOneRow(text, bv, text->refresh_row, text->refresh_y,
271                            yo, xo);
272                 // this because if we had a major update the refresh_row could
273                 // have been set to 0!
274                 if (text->refresh_row) {
275                         // otherwise this is called ONLY from BufferView_pimpl(update)
276                         // or we should see to set this flag accordingly
277                         if (text != bv->text)
278                                 text->status(bv, LyXText::UNCHANGED);
279                         expose(0, text->refresh_y - text->first_y + yo,
280                                    vwidth, text->refresh_row->height());
281                 }
282         }
283         break;
284         case LyXText::CHANGED_IN_DRAW: // just to remove the warning
285         case LyXText::UNCHANGED:
286                 // Nothing needs done
287                 break;
288         }
289
290         workarea().getPainter().end();
291 }
292
293
294 void LyXScreen::toggleSelection(LyXText * text, BufferView * bv,
295                                 bool kill_selection,
296                                 int yo, int xo)
297 {
298         // only if there is a selection
299         if (!text->selection.set()) return;
300
301         int const bottom = min(
302                 max(static_cast<int>(text->selection.end.y()
303                                      - text->selection.end.row()->baseline()
304                                      + text->selection.end.row()->height()),
305                     text->first_y),
306                 static_cast<int>(text->first_y + workarea().workHeight()));
307         int const top = min(
308                 max(static_cast<int>(text->selection.start.y() -
309                                      text->selection.start.row()->baseline()),
310                     text->first_y),
311                 static_cast<int>(text->first_y + workarea().workHeight()));
312
313         if (kill_selection)
314                 text->selection.set(false);
315
316         workarea().getPainter().start();
317
318         drawFromTo(text, bv, top - text->first_y, bottom - text->first_y,
319                    yo, xo);
320         expose(0, top - text->first_y,
321                workarea().workWidth(),
322                bottom - text->first_y - (top - text->first_y));
323
324         workarea().getPainter().end();
325 }
326
327
328 void LyXScreen::toggleToggle(LyXText * text, BufferView * bv,
329                              int yo, int xo)
330 {
331         if (text->toggle_cursor.par() == text->toggle_end_cursor.par()
332             && text->toggle_cursor.pos() == text->toggle_end_cursor.pos())
333                 return;
334
335         int const top_tmp = text->toggle_cursor.y()
336                 - text->toggle_cursor.row()->baseline();
337         int const bottom_tmp = text->toggle_end_cursor.y()
338                 - text->toggle_end_cursor.row()->baseline()
339                 + text->toggle_end_cursor.row()->height();
340
341         int const offset = yo < 0 ? yo : 0;
342         int const bottom = min(max(bottom_tmp, text->first_y),
343                 static_cast<int>(text->first_y + workarea().workHeight())) - offset;
344         int const top = min(max(top_tmp, text->first_y),
345                 static_cast<int>(text->first_y + workarea().workHeight())) - offset;
346
347         workarea().getPainter().start();
348
349         drawFromTo(text, bv, top - text->first_y,
350                    bottom - text->first_y, yo,
351                    xo);
352         expose(0, top - text->first_y, workarea().workWidth(),
353                bottom - text->first_y - (top - text->first_y));
354
355         workarea().getPainter().end();
356 }
357
358
359 void LyXScreen::redraw(LyXText * text, BufferView * bv)
360 {
361         greyed_out_ = !text;
362
363         workarea().getPainter().start();
364
365         if (greyed_out_) {
366                 greyOut();
367                 return;
368         }
369
370         drawFromTo(text, bv, 0, workarea().workHeight(), 0, 0, text == bv->text);
371         expose(0, 0, workarea().workWidth(), workarea().workHeight());
372
373         workarea().getPainter().end();
374
375         if (cursor_visible_) {
376                 cursor_visible_ = false;
377                 bv->showCursor();
378         }
379 }
380
381
382 void LyXScreen::greyOut()
383 {
384         if (!greyed_out_)
385                 return;
386
387         workarea().getPainter().fillRectangle(0, 0,
388                 workarea().workWidth(),
389                 workarea().workHeight(),
390                 LColor::bottomarea);
391
392         // Add a splash screen to the centre of the work area
393         SplashScreen const & splash = SplashScreen::get();
394         grfx::Image const * const splash_image = splash.image();
395         if (splash_image) {
396                 int const w = splash_image->getWidth();
397                 int const h = splash_image->getHeight();
398
399                 int x = (workarea().workWidth() - w) / 2;
400                 int y = (workarea().workHeight() - h) / 2;
401
402                 workarea().getPainter().image(x, y, w, h, *splash_image);
403
404                 string const & splash_text  = splash.text();
405                 LyXFont const & splash_font = splash.font();
406
407                 x += 260;
408                 y += 265;
409
410                 workarea().getPainter().text(x, y, splash_text, splash_font);
411         }
412         expose(0, 0, workarea().workWidth(), workarea().workHeight());
413         workarea().getPainter().end();
414 }
415
416
417 void LyXScreen::drawFromTo(LyXText * text, BufferView * bv,
418         int y1, int y2, int yo, int xo,
419         bool internal)
420 {
421         lyxerr[Debug::GUI] << "screen: drawFromTo " << y1 << '-' << y2 << endl;
422
423         int y_text = text->first_y + y1;
424
425         // get the first needed row
426         Row * row = text->getRowNearY(y_text);
427         // y_text is now the real beginning of the row
428
429         int y = y_text - text->first_y;
430         // y1 is now the real beginning of row on the screen
431
432         while (row != 0 && y < y2) {
433                 LyXText::text_status st = text->status();
434                 text->getVisibleRow(bv, y + yo,
435                                     xo, row, y + text->first_y);
436                 internal = internal && (st != LyXText::CHANGED_IN_DRAW);
437                 while (internal && text->status() == LyXText::CHANGED_IN_DRAW) {
438                         text->fullRebreak(bv);
439                         st = LyXText::NEED_MORE_REFRESH;
440                         text->setCursor(bv, text->cursor.par(), text->cursor.pos());
441                         text->status(bv, st);
442                         text->getVisibleRow(bv, y + yo,
443                                             xo, row, y + text->first_y);
444                 }
445                 y += row->height();
446                 row = row->next();
447         }
448         force_clear_ = false;
449
450         // maybe we have to clear the screen at the bottom
451         if ((y < y2) && text->isTopLevel()) {
452                 workarea().getPainter().fillRectangle(0, y,
453                         workarea().workWidth(), y2 - y,
454                         LColor::bottomarea);
455         }
456 }
457
458
459 void LyXScreen::drawOneRow(LyXText * text, BufferView * bv, Row * row,
460         int y_text, int yo, int xo)
461 {
462         int const y = y_text - text->first_y + yo;
463
464         if (((y + row->height()) > 0) &&
465             ((y - row->height()) <= static_cast<int>(workarea().workHeight()))) {
466                 text->getVisibleRow(bv, y, xo, row, y + text->first_y);
467         }
468         force_clear_ = false;
469 }