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