]> git.lyx.org Git - lyx.git/blob - src/frontends/WorkArea.cpp
SCons: msvc does not need this /TP option any more after we rename .C => .cpp. Also...
[lyx.git] / src / frontends / WorkArea.cpp
1 /**
2  * \file WorkArea.cpp
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  * \author Abdelrazak Younes
8  *
9  * Full author contact details are available in file CREDITS.
10  *
11  * Splash screen code added by Angus Leeming
12  */
13
14 #include <config.h>
15
16 #include "frontends/WorkArea.h"
17
18 #include "frontends/Application.h"
19 #include "frontends/FontMetrics.h"
20 #include "frontends/LyXView.h"
21
22 #include "BufferView.h"
23 #include "Buffer.h"
24 #include "BufferParams.h"
25 #include "Color.h"
26 #include "CoordCache.h"
27 #include "Cursor.h"
28 #include "debug.h"
29 #include "Font.h"
30 #include "FuncRequest.h"
31 #include "Language.h"
32 #include "LyX.h"
33 #include "LyXFunc.h"
34 #include "LyXRC.h"
35 #include "MetricsInfo.h"
36 #include "Text.h"
37
38 #include "gettext.h"
39 #include "support/ForkedcallsController.h"
40 #include "support/FileName.h"
41
42 #include <boost/utility.hpp>
43 #include <boost/bind.hpp>
44 #include <boost/current_function.hpp>
45
46 using lyx::support::ForkedcallsController;
47
48 using std::endl;
49 using std::min;
50 using std::max;
51 using std::string;
52
53
54 namespace {
55
56 // All the below connection objects are needed because of a bug in some
57 // versions of GCC (<=2.96 are on the suspects list.) By having and assigning
58 // to these connections we avoid a segfault upon startup, and also at exit.
59 // (Lgb)
60
61 boost::signals::connection timecon;
62
63 } // anon namespace
64
65 namespace lyx {
66 namespace frontend {
67
68 WorkArea::WorkArea(Buffer & buffer, LyXView & lv)
69         : buffer_view_(new BufferView(buffer)), lyx_view_(&lv),
70         cursor_visible_(false), cursor_timeout_(400)
71 {
72         // Setup the signals
73         timecon = cursor_timeout_.timeout
74                 .connect(boost::bind(&WorkArea::toggleCursor, this));
75
76         bufferChangedConnection_ =
77                 buffer.changed.connect(
78                         boost::bind(&WorkArea::redraw, this));
79
80         bufferClosingConnection_ =
81                 buffer.closing.connect(
82                 boost::bind(&WorkArea::close, this));
83
84         cursor_timeout_.start();
85 }
86
87
88 WorkArea::~WorkArea()
89 {
90         bufferChangedConnection_.disconnect();
91         bufferClosingConnection_.disconnect();
92
93         // current buffer is going to be switched-off, save cursor pos
94         // Ideally, the whole cursor stack should be saved, but session
95         // currently can only handle bottom (whole document) level pit and pos.
96         // That is to say, if a cursor is in a nested inset, it will be
97         // restore to the left of the top level inset.
98         Cursor & cur = buffer_view_->cursor();
99         LyX::ref().session().lastFilePos().save(
100                 support::FileName(buffer_view_->buffer().fileName()),
101                 boost::tie(cur.bottom().pit(), cur.bottom().pos()) );
102
103         delete buffer_view_;
104 }
105
106
107 void WorkArea::close()
108 {
109         lyx_view_->removeWorkArea(this);
110 }
111
112 //void WorkArea::setLyXView(LyXView * lyx_view)
113 //{
114 //      lyx_view_ = lyx_view;
115 //}
116
117
118 BufferView & WorkArea::bufferView()
119 {
120         return *buffer_view_;
121 }
122
123
124 BufferView const & WorkArea::bufferView() const
125 {
126         return *buffer_view_;
127 }
128
129
130 void WorkArea::stopBlinkingCursor()
131 {
132         cursor_timeout_.stop();
133         hideCursor();
134 }
135
136
137 void WorkArea::startBlinkingCursor()
138 {
139         showCursor();
140         cursor_timeout_.restart();
141 }
142
143
144 void WorkArea::redraw()
145 {
146         if (!isVisible())
147                 // No need to redraw in this case.
148                 return;
149
150         // No need to do anything if this is the current view. The BufferView
151         // metrics are already up to date.
152         if (lyx_view_ != theApp()->currentView()) {
153                 // FIXME: it would be nice to optimize for the off-screen case.
154                 buffer_view_->updateMetrics(false);
155                 buffer_view_->cursor().fixIfBroken();
156         }
157
158         updateScrollbar();
159
160         // update cursor position, because otherwise it has to wait until
161         // the blinking interval is over
162         if (cursor_visible_) {
163                 hideCursor();
164                 showCursor();
165         }
166         
167         ViewMetricsInfo const & vi = buffer_view_->viewMetricsInfo();
168
169         LYXERR(Debug::WORKAREA) << "WorkArea::redraw screen" << endl;
170
171         int const ymin = std::max(vi.y1, 0);
172         int const ymax = vi.p2 < vi.size - 1 ? vi.y2 : height();
173
174         expose(0, ymin, width(), ymax - ymin);
175
176         //LYXERR(Debug::WORKAREA)
177         //<< "  ymin = " << ymin << "  width() = " << width()
178 //              << "  ymax-ymin = " << ymax-ymin << std::endl;
179
180         if (lyxerr.debugging(Debug::WORKAREA))
181                 buffer_view_->coordCache().dump();
182 }
183
184
185 void WorkArea::processKeySym(KeySymbolPtr key, key_modifier::state state)
186 {
187         // In order to avoid bad surprise in the middle of an operation, we better stop
188         // the blinking cursor.
189         stopBlinkingCursor();
190
191         theLyXFunc().setLyXView(lyx_view_);
192         theLyXFunc().processKeySym(key, state);
193 }
194
195
196 void WorkArea::dispatch(FuncRequest const & cmd0, key_modifier::state k)
197 {
198         // Handle drag&drop
199         if (cmd0.action == LFUN_FILE_OPEN) {
200                 lyx_view_->dispatch(cmd0);
201                 return;
202         }
203
204         theLyXFunc().setLyXView(lyx_view_);
205
206         FuncRequest cmd;
207
208         if (cmd0.action == LFUN_MOUSE_PRESS) {
209                 if (k == key_modifier::shift)
210                         cmd = FuncRequest(cmd0, "region-select");
211                 else if (k == key_modifier::ctrl)
212                         cmd = FuncRequest(cmd0, "paragraph-select");
213                 else
214                         cmd = cmd0;
215         }
216         else
217                 cmd = cmd0;
218
219         // In order to avoid bad surprise in the middle of an operation, we better stop
220         // the blinking cursor.
221         if (!(cmd.action == LFUN_MOUSE_MOTION
222                 && cmd.button() == mouse_button::none))
223                 stopBlinkingCursor();
224
225         bool const needRedraw = buffer_view_->workAreaDispatch(cmd);
226
227         if (needRedraw)
228                 redraw();
229
230         // Skip these when selecting
231         if (cmd.action != LFUN_MOUSE_MOTION) {
232                 lyx_view_->updateLayoutChoice();
233                 lyx_view_->updateMenubar();
234                 lyx_view_->updateToolbars();
235         }
236
237         // GUI tweaks except with mouse motion with no button pressed.
238         if (!(cmd.action == LFUN_MOUSE_MOTION
239                 && cmd.button() == mouse_button::none)) {
240                 // Slight hack: this is only called currently when we
241                 // clicked somewhere, so we force through the display
242                 // of the new status here.
243                 lyx_view_->clearMessage();
244
245                 // Show the cursor immediately after any operation.
246                 startBlinkingCursor();
247         }
248 }
249
250
251 void WorkArea::resizeBufferView()
252 {
253         lyx_view_->busy(true);
254         lyx_view_->message(_("Formatting document..."));
255         buffer_view_->workAreaResize(width(), height());
256         lyx_view_->updateLayoutChoice();
257         lyx_view_->clearMessage();
258         lyx_view_->busy(false);
259 }
260
261
262 void WorkArea::updateScrollbar()
263 {
264         buffer_view_->updateScrollbar();
265         ScrollbarParameters const & scroll_ = buffer_view_->scrollbarParameters();
266         setScrollbarParams(scroll_.height, scroll_.position,
267                 scroll_.lineScrollHeight);
268 }
269
270
271 void WorkArea::scrollBufferView(int position)
272 {
273         stopBlinkingCursor();
274         buffer_view_->scrollDocView(position);
275         redraw();
276         if (lyxrc.cursor_follows_scrollbar) {
277                 buffer_view_->setCursorFromScrollbar();
278                 lyx_view_->updateLayoutChoice();
279         }
280         // Show the cursor immediately after any operation.
281         startBlinkingCursor();
282 }
283
284
285 void WorkArea::showCursor()
286 {
287         if (cursor_visible_)
288                 return;
289
290         CursorShape shape = BAR_SHAPE;
291
292         Text const & text = *buffer_view_->cursor().innerText();
293         Font const & realfont = text.real_current_font;
294         BufferParams const & bp = buffer_view_->buffer().params();
295         bool const samelang = realfont.language() == bp.language;
296         bool const isrtl = realfont.isVisibleRightToLeft();
297
298         if (!samelang || isrtl != bp.language->rightToLeft()) {
299                 shape = L_SHAPE;
300                 if (isrtl)
301                         shape = REVERSED_L_SHAPE;
302         }
303
304         // The ERT language hack needs fixing up
305         if (realfont.language() == latex_language)
306                 shape = BAR_SHAPE;
307
308         Font const font = buffer_view_->cursor().getFont();
309         FontMetrics const & fm = theFontMetrics(font);
310         int const asc = fm.maxAscent();
311         int const des = fm.maxDescent();
312         int h = asc + des;
313         int x = 0;
314         int y = 0;
315         buffer_view_->cursor().getPos(x, y);
316         y -= asc;
317
318         // if it doesn't touch the screen, don't try to show it
319         if (y + h < 0 || y >= height())
320                 return;
321
322         cursor_visible_ = true;
323         showCursor(x, y, h, shape);
324 }
325
326
327 void WorkArea::hideCursor()
328 {
329         if (!cursor_visible_)
330                 return;
331
332         cursor_visible_ = false;
333         removeCursor();
334 }
335
336
337 void WorkArea::toggleCursor()
338 {
339         if (cursor_visible_)
340                 hideCursor();
341         else
342                 showCursor();
343
344         // Use this opportunity to deal with any child processes that
345         // have finished but are waiting to communicate this fact
346         // to the rest of LyX.
347         ForkedcallsController & fcc = ForkedcallsController::get();
348         fcc.handleCompletedProcesses();
349
350         cursor_timeout_.restart();
351 }
352
353 } // namespace frontend
354 } // namespace lyx