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