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