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