]> git.lyx.org Git - lyx.git/blob - src/screen.C
Reverted the reverted changes - 1.
[lyx.git] / src / screen.C
1 /* This file is part of
2 * ====================================================== 
3
4 *           LyX, The Document Processor
5 *        
6 *           Copyright 1995 Matthias Ettrich
7 *           Copyright 1995-1998 The LyX Team
8 *
9 * ====================================================== */
10
11 #include <config.h>
12
13 #ifdef __GNUG__
14 #pragma implementation "lyxscreen.h"
15 #endif
16
17 #include <algorithm>
18
19 #include "lyxscreen.h"
20 #include "lyxtext.h"
21 #include "lyxrow.h"
22 #include "Painter.h"
23 #include "WorkArea.h"
24 #include "buffer.h"
25 #include "font.h"
26 #include "insets/insettext.h"
27 #include "ColorHandler.h"
28 #include "language.h"
29
30 using std::max;
31 using std::min;
32
33 namespace {
34
35 GC createGC()
36 {
37         XGCValues val;
38         val.foreground = BlackPixel(fl_get_display(), 
39                                     DefaultScreen(fl_get_display()));
40         
41         val.function = GXcopy;
42         val.graphics_exposures = false;
43         val.line_style = LineSolid;
44         val.line_width = 0;
45         return XCreateGC(fl_get_display(), RootWindow(fl_get_display(), 0), 
46                          GCForeground | GCFunction | GCGraphicsExposures
47                          | GCLineWidth | GCLineStyle , &val);
48 }
49
50 } // namespace anon
51
52
53 // Constructor
54 LyXScreen::LyXScreen(WorkArea & o)
55         : owner(o), force_clear(true)
56 {
57         // the cursor isnt yet visible
58         cursor_visible = false;
59         cursor_pixmap = 0;
60         cursor_pixmap_x = 0;
61         cursor_pixmap_y = 0;
62         cursor_pixmap_w = 0;
63         cursor_pixmap_h = 0;
64
65         // We need this GC
66         gc_copy = createGC();
67 }
68
69
70 void LyXScreen::setCursorColor() 
71 {
72         if (!lyxColorHandler.get()) return;
73
74         GC gc = lyxColorHandler->getGCForeground(LColor::cursor);
75         
76         XGCValues val;
77         XGetGCValues(fl_get_display(),
78                      gc, GCForeground, &val);
79         XChangeGC(fl_get_display(), gc_copy, GCForeground, &val);
80 }
81
82
83 void LyXScreen::Redraw(LyXText * text, BufferView * bv)
84 {
85         DrawFromTo(text, bv, 0, owner.height());
86         expose(0, 0, owner.workWidth(), owner.height());
87         if (cursor_visible) {
88                 cursor_visible = false;
89                 ShowCursor(text, bv);
90         }
91 }
92
93
94 void LyXScreen::expose(int x, int y, int exp_width, int exp_height)
95 {
96         XCopyArea(fl_get_display(),
97                   owner.getPixmap(),
98                   owner.getWin(),
99                   gc_copy,
100                   x, y,
101                   exp_width, exp_height,
102                   x + owner.xpos(),
103                   y + owner.ypos());
104 }
105
106
107 void LyXScreen::DrawFromTo(LyXText * text, BufferView * bv,
108                            int y1, int y2, int y_offset, int x_offset)
109 {
110         int y_text = text->first + y1;
111    
112         // get the first needed row 
113         Row * row = text->GetRowNearY(y_text);
114         // y_text is now the real beginning of the row
115    
116         int y = y_text - text->first;
117         // y1 is now the real beginning of row on the screen
118         
119         while (row != 0 && y < y2) {
120                 LyXText::text_status st = bv->text->status;
121                 do {
122                         bv->text->status = st;
123                         text->GetVisibleRow(bv, y + y_offset,
124                                             x_offset, row, y + text->first);
125                 } while (bv->text->status == LyXText::CHANGED_IN_DRAW);
126                 bv->text->status = st;
127                 y += row->height();
128                 row = row->next();
129         }
130         force_clear = false;
131
132         // maybe we have to clear the screen at the bottom
133         if ((y < y2) && text->bv_owner) {
134                 owner.getPainter().fillRectangle(0, y,
135                                                  owner.workWidth(),
136                                                  y2 - y,
137                                                LColor::bottomarea);
138         }
139 }
140
141
142 void LyXScreen::DrawOneRow(LyXText * text, BufferView * bv, Row * row,
143                            int y_text, int y_offset, int x_offset)
144 {
145         int const y = y_text - text->first + y_offset;
146
147         if (((y + row->height()) > 0) &&
148             ((y - row->height()) <= static_cast<int>(owner.height()))) {
149                 // ok there is something visible
150                 LyXText::text_status st = bv->text->status;
151                 do {
152                         bv->text->status = st;
153                         text->GetVisibleRow(bv, y, x_offset, row,
154                                             y + text->first);
155                 } while (bv->text->status == LyXText::CHANGED_IN_DRAW);
156                 bv->text->status = st;
157         }
158         force_clear = false;
159 }
160
161
162 /* draws the screen, starting with textposition y. uses as much already
163 * printed pixels as possible */
164 void LyXScreen::Draw(LyXText * text, BufferView * bv, unsigned int y)
165 {
166         if (cursor_visible) HideCursor();
167
168         int const old_first = text->first;
169         text->first = y;
170
171         // is any optimiziation possible?
172         if ((y - old_first) < owner.height()
173             && (old_first - y) < owner.height()) {
174                 if (text->first < old_first) {
175                         DrawFromTo(text, bv, 0, old_first - text->first);
176                         XCopyArea (fl_get_display(),
177                                    owner.getWin(),
178                                    owner.getWin(),
179                                    gc_copy,
180                                    owner.xpos(),
181                                    owner.ypos(),
182                                    owner.workWidth(),
183                                    owner.height() - old_first + text->first,
184                                    owner.xpos(),
185                                    owner.ypos() + old_first - text->first
186                                 );
187                         // expose the area drawn
188                         expose(0, 0,
189                                owner.workWidth(),
190                                old_first - text->first);
191                 } else  {
192                         DrawFromTo(text, bv,
193                                    owner.height() + old_first - text->first,
194                                    owner.height());
195                         XCopyArea (fl_get_display(),
196                                    owner.getWin(),
197                                    owner.getWin(),
198                                    gc_copy,
199                                    owner.xpos(),
200                                    owner.ypos() + text->first - old_first,
201                                    owner.workWidth(),
202                                    owner.height() + old_first - text->first,
203                                    owner.xpos(),
204                                    owner.ypos());
205                         // expose the area drawn
206                         expose(0, owner.height() + old_first - text->first,
207                                owner.workWidth(), text->first - old_first);
208                 }
209         } else {
210                 // make a dumb new-draw 
211                 DrawFromTo(text, bv, 0, owner.height());
212                 expose(0, 0, owner.workWidth(), owner.height());
213         }
214 }
215
216
217 void LyXScreen::ShowCursor(LyXText const * text, BufferView const * bv)
218 {
219         if (!cursor_visible) {
220                 Cursor_Shape shape = BAR_SHAPE;
221                 if (text->real_current_font.language() !=
222                     bv->buffer()->params.language
223                     || text->real_current_font.isVisibleRightToLeft()
224                     != bv->buffer()->params.language->RightToLeft())
225                         shape = (text->real_current_font.isVisibleRightToLeft())
226                                 ? REVERSED_L_SHAPE : L_SHAPE;
227                 ShowManualCursor(text, text->cursor.x(), text->cursor.y(),
228                                  lyxfont::maxAscent(text->real_current_font),
229                                  lyxfont::maxDescent(text->real_current_font),
230                                  shape);
231         }
232 }
233
234
235 /* returns true if first has changed, otherwise false */ 
236 bool LyXScreen::FitManualCursor(LyXText * text, BufferView * bv,
237                                 int /*x*/, int y, int asc, int desc)
238 {
239         int newtop = text->first;
240   
241         if (y + desc - text->first >= static_cast<int>(owner.height()))
242                 newtop = y - 3 * owner.height() / 4;  // the scroll region must be so big!!
243         else if (y - asc < static_cast<int>(text->first)
244                 && text->first > 0) {
245                 newtop = y - owner.height() / 4;
246         }
247
248         newtop = max(newtop, 0); // can newtop ever be < 0? (Lgb)
249   
250         if (newtop != static_cast<int>(text->first)) {
251                 Draw(text, bv, newtop);
252                 text->first = newtop;
253                 return true;
254         }
255         return false;
256 }
257
258
259 void LyXScreen::ShowManualCursor(LyXText const * text, int x, int y,
260                                  int asc, int desc, Cursor_Shape shape)
261 {
262         // Update the cursor color.
263         setCursorColor();
264         
265         int const y1 = max(y - text->first - asc, 0);
266         int const y_tmp = min(y - text->first + desc,
267                               static_cast<int>(owner.height()));
268
269         // Secure against very strange situations
270         int const y2 = max(y_tmp, y1);
271         
272         if (cursor_pixmap){
273                 XFreePixmap(fl_get_display(), cursor_pixmap);
274                 cursor_pixmap = 0;
275         }
276
277         if (y2 > 0 && y1 < int(owner.height())) {
278                 cursor_pixmap_h = y2 - y1 + 1;
279                 cursor_pixmap_y = y1;
280
281                 switch (shape) {
282                 case BAR_SHAPE:
283                         cursor_pixmap_w = 1;
284                         cursor_pixmap_x = x;
285                         break;
286                 case L_SHAPE:
287                         cursor_pixmap_w = cursor_pixmap_h/3;
288                         cursor_pixmap_x = x;
289                         break;
290                 case REVERSED_L_SHAPE:
291                         cursor_pixmap_w = cursor_pixmap_h/3;
292                         cursor_pixmap_x = x - cursor_pixmap_w + 1;
293                         break;
294                 }
295
296                 cursor_pixmap = 
297                         XCreatePixmap (fl_get_display(),
298                                        fl_root,
299                                        cursor_pixmap_w,
300                                        cursor_pixmap_h,
301                                        fl_get_visual_depth());
302                 XCopyArea (fl_get_display(),
303                            owner.getWin(),
304                            cursor_pixmap,
305                            gc_copy,
306                            owner.xpos() + cursor_pixmap_x,
307                            owner.ypos() + cursor_pixmap_y,
308                            cursor_pixmap_w,
309                            cursor_pixmap_h,
310                            0, 0);
311                 XDrawLine(fl_get_display(),
312                           owner.getWin(),
313                           gc_copy,
314                           x + owner.xpos(),
315                           y1 + owner.ypos(),
316                           x + owner.xpos(),
317                           y2 + owner.ypos());
318                 switch (shape) {
319                 case BAR_SHAPE:
320                         break;
321                 case L_SHAPE:
322                 case REVERSED_L_SHAPE:
323                         int const rectangle_h = (cursor_pixmap_h + 10) / 20;
324                         XFillRectangle(fl_get_display(),
325                                        owner.getWin(),
326                                        gc_copy,
327                                        cursor_pixmap_x + owner.xpos(),
328                                        y2 - rectangle_h + 1 + owner.ypos(),
329                                        cursor_pixmap_w - 1, rectangle_h);
330                         break;
331                 }
332
333         }
334         cursor_visible = true;
335 }
336
337
338 void LyXScreen::HideCursor()
339 {
340         if (!cursor_visible) return;
341
342         if (cursor_pixmap){
343                 XCopyArea (fl_get_display(), 
344                            cursor_pixmap,
345                            owner.getWin(),
346                            gc_copy,
347                            0, 0, 
348                            cursor_pixmap_w, cursor_pixmap_h,
349                            cursor_pixmap_x + owner.xpos(),
350                            cursor_pixmap_y + owner.ypos());
351         }
352         cursor_visible = false;
353 }
354
355
356 void LyXScreen::CursorToggle(LyXText const * text, BufferView const * bv)
357 {
358         if (cursor_visible)
359                 HideCursor();
360         else
361                 ShowCursor(text, bv);
362 }
363
364
365 /* returns a new top so that the cursor is visible */ 
366 unsigned int LyXScreen::TopCursorVisible(LyXText const * text)
367 {
368         int newtop = text->first;
369
370         if (text->cursor.y()
371             - text->cursor.row()->baseline()
372             + text->cursor.row()->height()
373             - text->first >= owner.height()) {
374                 if (text->cursor.row()->height() < owner.height()
375                     && text->cursor.row()->height() > owner.height() / 4)
376                         newtop = text->cursor.y()
377                                 + text->cursor.row()->height()
378                                 - text->cursor.row()->baseline() - owner.height();
379                 else
380                         newtop = text->cursor.y()
381                                 - 3 * owner.height() / 4;   /* the scroll region must be so big!! */
382         } else if (static_cast<int>((text->cursor.y()) - text->cursor.row()->baseline()) <
383                    text->first && text->first > 0)
384         {
385                 if (text->cursor.row()->height() < owner.height()
386                     && text->cursor.row()->height() > owner.height() / 4)
387                         newtop = text->cursor.y() - text->cursor.row()->baseline();
388                 else {
389                         newtop = text->cursor.y() - owner.height() / 4;
390                         newtop = min(newtop, int(text->first));
391                 }
392         }
393
394         newtop = max(newtop, 0);
395
396         return newtop;
397 }
398
399
400 /* scrolls the screen so that the cursor is visible, if necessary.
401 * returns true if a change was made, otherwise false */ 
402 bool LyXScreen::FitCursor(LyXText * text, BufferView * bv)
403 {
404         // Is a change necessary?
405         int const newtop = TopCursorVisible(text);
406         bool const result = (newtop != text->first);
407         if (result)
408                 Draw(text, bv, newtop);
409         return result;
410 }
411
412    
413 void LyXScreen::Update(LyXText * text, BufferView * bv,
414                        int y_offset, int x_offset)
415 {
416         switch (text->status) {
417         case LyXText::NEED_MORE_REFRESH:
418         {
419                 int const y = max(int(text->refresh_y - text->first), 0);
420                 DrawFromTo(text, bv, y, owner.height(), y_offset, x_offset);
421                 text->refresh_y = 0;
422                 text->status = LyXText::UNCHANGED;
423                 expose(0, y, owner.workWidth(), owner.height() - y);
424         }
425         break;
426         case LyXText::NEED_VERY_LITTLE_REFRESH:
427         {
428                 // ok I will update the current cursor row
429                 DrawOneRow(text, bv, text->refresh_row, text->refresh_y,
430                            y_offset, x_offset);
431                 text->status = LyXText::UNCHANGED;
432                 expose(0, text->refresh_y - text->first + y_offset,
433                        owner.workWidth(), text->refresh_row->height());
434         }
435         break;
436         case LyXText::CHANGED_IN_DRAW: // just to remove the warning
437         case LyXText::UNCHANGED:
438                 // Nothing needs done
439                 break;
440         }
441 }
442
443
444 void LyXScreen::ToggleSelection(LyXText * text, BufferView * bv,
445                                 bool kill_selection,
446                                 int y_offset, int x_offset)
447 {
448         // only if there is a selection
449         if (!text->selection) return;
450
451         int const bottom = min(
452                 max(static_cast<int>(text->sel_end_cursor.y()
453                                      - text->sel_end_cursor.row()->baseline()
454                                      + text->sel_end_cursor.row()->height()),
455                     text->first),
456                 static_cast<int>(text->first + owner.height()));
457         int const top = min(
458                 max(static_cast<int>(text->sel_start_cursor.y() -
459                                      text->sel_start_cursor.row()->baseline()),
460                     text->first),
461                 static_cast<int>(text->first + owner.height()));
462
463         if (kill_selection)
464                 text->selection = 0;
465         DrawFromTo(text, bv, top - text->first, bottom - text->first,
466                    y_offset, x_offset);
467         expose(0, top - text->first,
468                owner.workWidth(),
469                bottom - text->first - (top - text->first));
470 }
471   
472    
473 void LyXScreen::ToggleToggle(LyXText * text, BufferView * bv,
474                              int y_offset, int x_offset)
475 {
476         if (text->toggle_cursor.par() == text->toggle_end_cursor.par()
477             && text->toggle_cursor.pos() == text->toggle_end_cursor.pos())
478                 return;
479         
480         int const top_tmp = text->toggle_cursor.y()
481                 - text->toggle_cursor.row()->baseline();
482         int const bottom_tmp = text->toggle_end_cursor.y()
483                 - text->toggle_end_cursor.row()->baseline() 
484                 + text->toggle_end_cursor.row()->height();
485         
486         int const bottom = min(max(bottom_tmp, text->first),
487                      static_cast<int>(text->first + owner.height()));
488         int const top = min(max(top_tmp, text->first),
489                   static_cast<int>(text->first + owner.height()));
490
491         DrawFromTo(text, bv, top - text->first, bottom - text->first, y_offset,
492                    x_offset);
493         expose(0, top - text->first, owner.workWidth(),
494                bottom - text->first - (top - text->first));
495 }