]> git.lyx.org Git - lyx.git/blob - src/frontends/screen.C
b9720d3be524e632fbdda6dd761cd50467bd1f6a
[lyx.git] / src / frontends / screen.C
1 /**
2  * \file screen.C
3  * Copyright 2002 the LyX Team
4  * Read the file COPYING
5  *
6  * \author John Levon <moz@compsoc.man.ac.uk>
7  */
8
9 #ifdef __GNUG__
10 #pragma implementation
11 #endif
12
13 #include <config.h>
14
15 #include "screen.h"
16 #include "lyxtext.h"
17 #include "lyxrow.h"
18 #include "BufferView.h"
19 #include "buffer.h"
20 #include "WorkArea.h"
21 #include "Painter.h"
22 #include "font_metrics.h"
23 #include "language.h"
24 #include "debug.h"
25
26 using std::min;
27 using std::max;
28 using std::endl;
29
30
31 LyXScreen::LyXScreen()
32         : force_clear_(true), cursor_visible_(false)
33 {
34 }
35
36
37 LyXScreen::~LyXScreen()
38 {
39 }
40
41 // FIXME: GUII these cursor methods need to decide
42 // whether the workarea is focused or not
43
44 void LyXScreen::showCursor(LyXText const * text, BufferView const * bv)
45 {
46         if (cursor_visible_)
47                 return;
48
49         workarea().getPainter().start();
50
51         Cursor_Shape shape = BAR_SHAPE;
52         BufferParams const & bp(bv->buffer()->params);
53         LyXFont const & realfont(text->real_current_font);
54
55         if (realfont.language() != bp.language
56                 || realfont.isVisibleRightToLeft()
57                 != bp.language->RightToLeft()) {
58                 shape = (realfont.isVisibleRightToLeft())
59                         ? REVERSED_L_SHAPE : L_SHAPE;
60         }
61
62         showManualCursor(text, text->cursor.x(), text->cursor.y(),
63                 font_metrics::maxAscent(realfont),
64                 font_metrics::maxDescent(realfont),
65                 shape);
66
67         workarea().getPainter().end();
68 }
69
70
71 bool LyXScreen::fitManualCursor(BufferView * bv, LyXText * text,
72         int /*x*/, int y, int asc, int desc)
73 {
74         int const vheight = workarea().workHeight();
75         int newtop = text->first_y;
76
77         if (y + desc - text->first_y >= vheight)
78                 newtop = y - 3 * vheight / 4;  // the scroll region must be so big!!
79         else if (y - asc < text->first_y
80                 && text->first_y > 0) {
81                 newtop = y - vheight / 4;
82         }
83
84         newtop = max(newtop, 0); // can newtop ever be < 0? (Lgb)
85
86         if (newtop != text->first_y) {
87                 draw(text, bv, newtop);
88                 text->first_y = newtop;
89                 return true;
90         }
91         return false;
92 }
93
94
95 void LyXScreen::cursorToggle(BufferView * bv) const
96 {
97         if (cursor_visible_)
98                 bv->hideCursor();
99         else
100                 bv->showCursor();
101 }
102
103
104 unsigned int LyXScreen::topCursorVisible(LyXCursor const & cursor, int top_y)
105 {
106         int const vheight = workarea().workHeight();
107         int newtop = top_y;
108
109         Row * row = cursor.row();
110
111         // Is this a hack? Yes, probably... (Lgb)
112         if (!row)
113                 return max(newtop, 0);
114
115         if (cursor.y() - row->baseline() + row->height()
116             - top_y >= vheight) {
117                 if (row->height() < vheight
118                     && row->height() > vheight / 4) {
119                         newtop = cursor.y()
120                                 + row->height()
121                                 - row->baseline() - vheight;
122                 } else {
123                         // scroll down
124                         newtop = cursor.y()
125                                 - vheight / 2;   /* the scroll region must be so big!! */
126                 }
127
128         } else if (static_cast<int>((cursor.y()) - row->baseline()) <
129                    top_y && top_y > 0) {
130                 if (row->height() < vheight
131                     && row->height() > vheight / 4) {
132                         newtop = cursor.y() - row->baseline();
133                 } else {
134                         // scroll up
135                         newtop = cursor.y() - vheight / 2;
136                         newtop = min(newtop, top_y);
137                 }
138         }
139
140         newtop = max(newtop, 0);
141
142         return newtop;
143 }
144
145
146 bool LyXScreen::fitCursor(LyXText * text, BufferView * bv)
147 {
148         // Is a change necessary?
149         int const newtop = topCursorVisible(text->cursor, text->first_y);
150         bool const result = (newtop != text->first_y);
151         if (result)
152                 draw(text, bv, newtop);
153         return result;
154 }
155
156
157 void LyXScreen::update(LyXText * text, BufferView * bv,
158         int yo, int xo)
159 {
160         int const vwidth = workarea().workWidth();
161         int const vheight = workarea().workHeight();
162
163         workarea().getPainter().start();
164
165         switch (text->status()) {
166         case LyXText::NEED_MORE_REFRESH:
167         {
168                 int const y = max(int(text->refresh_y - text->first_y), 0);
169                 drawFromTo(text, bv, y, vheight, yo, xo);
170                 text->refresh_y = 0;
171                 // otherwise this is called ONLY from BufferView_pimpl(update)
172                 // or we should see to set this flag accordingly
173                 if (text != bv->text)
174                         text->status(bv, LyXText::UNCHANGED);
175                 expose(0, y, vwidth, vheight - y);
176         }
177         break;
178         case LyXText::NEED_VERY_LITTLE_REFRESH:
179         {
180                 // ok I will update the current cursor row
181                 drawOneRow(text, bv, text->refresh_row, text->refresh_y,
182                            yo, xo);
183                 // this because if we had a major update the refresh_row could
184                 // have been set to 0!
185                 if (text->refresh_row) {
186                         // otherwise this is called ONLY from BufferView_pimpl(update)
187                         // or we should see to set this flag accordingly
188                         if (text != bv->text)
189                                 text->status(bv, LyXText::UNCHANGED);
190                         expose(0, text->refresh_y - text->first_y + yo,
191                                    vwidth, text->refresh_row->height());
192                 }
193         }
194         break;
195         case LyXText::CHANGED_IN_DRAW: // just to remove the warning
196         case LyXText::UNCHANGED:
197                 // Nothing needs done
198                 break;
199         }
200
201         workarea().getPainter().end();
202 }
203
204
205 void LyXScreen::toggleSelection(LyXText * text, BufferView * bv,
206                                 bool kill_selection,
207                                 int yo, int xo)
208 {
209         // only if there is a selection
210         if (!text->selection.set()) return;
211
212         int const bottom = min(
213                 max(static_cast<int>(text->selection.end.y()
214                                      - text->selection.end.row()->baseline()
215                                      + text->selection.end.row()->height()),
216                     text->first_y),
217                 static_cast<int>(text->first_y + workarea().workHeight()));
218         int const top = min(
219                 max(static_cast<int>(text->selection.start.y() -
220                                      text->selection.start.row()->baseline()),
221                     text->first_y),
222                 static_cast<int>(text->first_y + workarea().workHeight()));
223
224         if (kill_selection)
225                 text->selection.set(false);
226
227         workarea().getPainter().start();
228
229         drawFromTo(text, bv, top - text->first_y, bottom - text->first_y,
230                    yo, xo);
231         expose(0, top - text->first_y,
232                workarea().workWidth(),
233                bottom - text->first_y - (top - text->first_y));
234
235         workarea().getPainter().end();
236 }
237
238
239 void LyXScreen::toggleToggle(LyXText * text, BufferView * bv,
240                              int yo, int xo)
241 {
242         if (text->toggle_cursor.par() == text->toggle_end_cursor.par()
243             && text->toggle_cursor.pos() == text->toggle_end_cursor.pos())
244                 return;
245
246         int const top_tmp = text->toggle_cursor.y()
247                 - text->toggle_cursor.row()->baseline();
248         int const bottom_tmp = text->toggle_end_cursor.y()
249                 - text->toggle_end_cursor.row()->baseline()
250                 + text->toggle_end_cursor.row()->height();
251
252         int const offset = yo < 0 ? yo : 0;
253         int const bottom = min(max(bottom_tmp, text->first_y),
254                 static_cast<int>(text->first_y + workarea().workHeight())) - offset;
255         int const top = min(max(top_tmp, text->first_y),
256                 static_cast<int>(text->first_y + workarea().workHeight())) - offset;
257
258         workarea().getPainter().start();
259
260         drawFromTo(text, bv, top - text->first_y,
261                    bottom - text->first_y, yo,
262                    xo);
263         expose(0, top - text->first_y, workarea().workWidth(),
264                bottom - text->first_y - (top - text->first_y));
265
266         workarea().getPainter().end();
267 }
268
269
270 void LyXScreen::redraw(LyXText * text, BufferView * bv)
271 {
272         workarea().getPainter().start();
273
274         if (!text) {
275                 greyOut();
276                 expose(0, 0, workarea().workWidth(), workarea().workHeight());
277                 workarea().getPainter().end();
278                 return;
279         }
280
281         drawFromTo(text, bv, 0, workarea().workHeight(), 0, 0, text == bv->text);
282         expose(0, 0, workarea().workWidth(), workarea().workHeight());
283
284         workarea().getPainter().end();
285
286         if (cursor_visible_) {
287                 cursor_visible_ = false;
288                 bv->showCursor();
289         }
290 }
291
292
293 void LyXScreen::greyOut()
294 {
295         workarea().getPainter().fillRectangle(0, 0,
296                 workarea().workWidth(),
297                 workarea().workHeight(),
298                 LColor::bottomarea);
299
300 // FIXME: pending GUIIzation / cleanup of graphics cache.
301 //        We should be using something like this.
302 #if 0
303         static bool first = true;
304         if (first) {
305                 first = false;
306
307                 splash_file_ = (lyxrc.show_banner) ?
308                         LibFileSearch("images", "banner", "xpm") : string();
309                 if (splash_file_) {
310                         grfx::GCache & gc = grfx::GCache::get();
311                         gc.add(splash_file_);
312                         gc.startLoading(splash_file_);
313                 }
314         }
315
316         // Add a splash screen to the centre of the work area
317         grfx::GCache & gc = grfx::GCache::get();
318         grfx::ImagePtr const splash = gc.image(splash_file_);
319         if (splash.get()) {
320                 int const w = splash->getWidth();
321                 int const h = splash->getHeight();
322
323                 int const x = 0.5 * (workarea().workWidth() - w);
324                 int const y = 0.5 * (workarea().workHeight() - h);
325
326                 workarea().getPainter().image(x, y, w, h, splash->getPixmap());
327         }
328 #endif
329 // Alternatively, we should compile this into the code.
330 // I think that that is better here (so that the pixmap is displayed on
331 // start-up).
332 // Would need a new method
333 //      virtual Pixmap splashPixmap() = 0;
334 // or some such.
335 // Angus 21 June 2002
336 }
337
338
339 void LyXScreen::drawFromTo(LyXText * text, BufferView * bv,
340         int y1, int y2, int yo, int xo,
341         bool internal)
342 {
343         lyxerr[Debug::GUI] << "screen: drawFromTo " << y1 << "-" << y2 << endl;
344
345         int y_text = text->first_y + y1;
346
347         // get the first needed row
348         Row * row = text->getRowNearY(y_text);
349         // y_text is now the real beginning of the row
350
351         int y = y_text - text->first_y;
352         // y1 is now the real beginning of row on the screen
353
354         while (row != 0 && y < y2) {
355                 LyXText::text_status st = text->status();
356                 text->getVisibleRow(bv, y + yo,
357                                     xo, row, y + text->first_y);
358                 internal = internal && (st != LyXText::CHANGED_IN_DRAW);
359                 while (internal && text->status() == LyXText::CHANGED_IN_DRAW) {
360                         text->fullRebreak(bv);
361                         st = LyXText::NEED_MORE_REFRESH;
362                         text->setCursor(bv, text->cursor.par(), text->cursor.pos());
363                         text->status(bv, st);
364                         text->getVisibleRow(bv, y + yo,
365                                             xo, row, y + text->first_y);
366                 }
367                 y += row->height();
368                 row = row->next();
369         }
370         force_clear_ = false;
371
372         // maybe we have to clear the screen at the bottom
373         if ((y < y2) && text->bv_owner) {
374                 workarea().getPainter().fillRectangle(0, y,
375                         workarea().workWidth(), y2 - y,
376                         LColor::bottomarea);
377         }
378 }
379
380
381 void LyXScreen::drawOneRow(LyXText * text, BufferView * bv, Row * row,
382         int y_text, int yo, int xo)
383 {
384         int const y = y_text - text->first_y + yo;
385
386         if (((y + row->height()) > 0) &&
387             ((y - row->height()) <= static_cast<int>(workarea().workHeight()))) {
388                 text->getVisibleRow(bv, y, xo, row, y + text->first_y);
389         }
390         force_clear_ = false;
391 }