]> git.lyx.org Git - lyx.git/blob - src/WorkArea.C
Dekel's patch -- I didn't fix the xforms-0.88 keysyms stuff so it still doesn't finis...
[lyx.git] / src / WorkArea.C
1 /* This file is part of
2  * ======================================================
3  * 
4  *           LyX, The Document Processor
5  *        
6  *           Copyright 1995 Matthias Ettrich
7  *           Copyright 1995-2000 The LyX Team.
8  *
9  * ====================================================== */
10
11 #include <config.h>
12 #include <cmath>
13
14 #ifdef __GNUG__
15 #pragma implementation
16 #endif
17
18 #include "WorkArea.h"
19 #include "debug.h"
20 #include "support/lstrings.h"
21 #include "BufferView.h"
22 #include "LyXView.h"
23 #include "lyxfunc.h"
24
25 using std::endl;
26
27 FL_OBJECT * figinset_canvas;
28
29 // needed to make the c++ compiler find the correct version of abs.
30 // This is at least true for g++.
31 using std::abs;
32
33 static inline
34 void waitForX()
35 {
36         XSync(fl_get_display(), 0);
37 }
38
39 extern "C" {
40 // Just a bunch of C wrappers around static members of WorkArea
41         void C_WorkArea_scroll_cb(FL_OBJECT * ob, long buf)
42         {
43                 WorkArea::scroll_cb(ob, buf);
44         }
45
46         int C_WorkArea_work_area_handler(FL_OBJECT * ob, int event,
47                                            FL_Coord, FL_Coord, 
48                                            int key, void * xev)
49         {
50                 return WorkArea::work_area_handler(ob, event,
51                                                    0, 0, key, xev);
52         }
53 }
54
55
56
57 WorkArea::WorkArea(BufferView * o, int xpos, int ypos, int width, int height)
58         : owner_(o), workareapixmap(0), painter_(*this)
59 {
60         fl_freeze_all_forms();
61
62         figinset_canvas = 0;
63
64         if (lyxerr.debugging(Debug::GUI))
65                 lyxerr << "Creating work area: +"
66                        << xpos << '+' << ypos << ' '
67                        << width << 'x' << height << endl;
68         //
69         FL_OBJECT * obj;
70         const int bw = int(std::abs(float(fl_get_border_width())));
71
72         // We really want to get rid of figinset_canvas.
73         ::figinset_canvas = figinset_canvas = obj =
74                   fl_add_canvas(FL_NORMAL_CANVAS,
75                                 xpos + 1, ypos + 1, 1, 1, "");
76         fl_set_object_boxtype(obj, FL_NO_BOX);
77         fl_set_object_resize(obj, FL_RESIZE_ALL);
78         fl_set_object_gravity(obj, NorthWestGravity, NorthWestGravity);
79         
80         // a box
81         if (lyxerr.debugging(Debug::GUI))
82                 lyxerr << "\tbackground box: +"
83                        << xpos << '+' << ypos << ' '
84                        << width - 15 << 'x' << height << endl;
85         backgroundbox = obj = fl_add_box(FL_BORDER_BOX,
86                                          xpos, ypos,
87                                          width - 15,
88                                          height,"");
89         fl_set_object_resize(obj, FL_RESIZE_ALL);
90         fl_set_object_gravity(obj, NorthWestGravity, SouthEastGravity);
91
92         //
93         // THE SCROLLBAR
94         //
95
96         scrollbar = obj = fl_add_scrollbar(FL_VERT_SCROLLBAR,
97                                            xpos + width - 15,
98                                            ypos, 17, height, "");
99         fl_set_object_boxtype(obj, FL_UP_BOX);
100         fl_set_object_resize(obj, FL_RESIZE_ALL);
101         fl_set_object_gravity(obj, NorthEastGravity, SouthEastGravity);
102         obj->u_vdata = this;
103         fl_set_object_callback(obj, C_WorkArea_scroll_cb, 0);
104
105         ///
106         /// The free object
107
108         // Create the workarea pixmap
109         createPixmap(width - 15 - 2 * bw, height - 2 * bw);
110
111         // We add this object as late as possible to avoit problems
112         // with drawing.
113         if (lyxerr.debugging(Debug::GUI))
114                 lyxerr << "\tfree object: +"
115                        << xpos + bw << '+' << ypos + bw << ' '
116                        << width - 15 - 2 * bw << 'x'
117                        << height - 2 * bw << endl;
118         
119         work_area = obj = fl_add_free(FL_INPUT_FREE,
120                                       xpos + bw, ypos + bw,
121                                       width - 15 - 2 * bw, // scrollbarwidth
122                                       height - 2 * bw, "",
123                                       C_WorkArea_work_area_handler);
124         //obj->wantkey = FL_KEY_TAB;
125         obj->wantkey = FL_KEY_ALL;
126         obj->u_vdata = this; /* This is how we pass the WorkArea
127                                        to the work_area_handler. */
128         fl_set_object_boxtype(obj,FL_DOWN_BOX);
129         fl_set_object_resize(obj, FL_RESIZE_ALL);
130         fl_set_object_gravity(obj, NorthWestGravity, SouthEastGravity);
131
132         fl_unfreeze_all_forms();
133 }
134
135
136 WorkArea::~WorkArea()
137 {
138         if (workareapixmap)
139                 XFreePixmap(fl_display, workareapixmap);
140 }
141
142
143 bool WorkArea::belowMouse() const
144 {
145         FL_Coord x, y;
146         unsigned int button;
147         fl_get_mouse(&x, &y, &button);
148         FL_Coord ulx = work_area->form->x + work_area->x;
149         FL_Coord uly = work_area->form->y + work_area->y;
150         FL_Coord w = work_area->w;
151         FL_Coord h = work_area->h;
152         if (x > ulx && y > uly && x < ulx + h && y < uly + w)
153                 return true;
154         return false;
155         
156         
157         //lyxerr << "Mouse: (" << x << ", " << y <<") button = " << button << endl;
158         //lyxerr << "Workarea: (" << work_area->x + work_area->form->x << ", " << work_area->y + work_area->form->y << ", " << work_area->w << ", " << work_area->h << ")" << endl;
159         //lyxerr << "Below mouse: " << work_area->belowmouse << endl;
160         //return work_area->belowmouse;
161 }
162
163
164 void WorkArea::resize(int xpos, int ypos, int width, int height)
165 {
166         fl_freeze_all_forms();
167         
168         const int bw = int(std::abs(float(fl_get_border_width())));
169
170         // a box
171         fl_set_object_geometry(backgroundbox, xpos, ypos, width - 15, height);
172         
173         //
174         // THE SCROLLBAR
175         //
176         fl_set_object_geometry(scrollbar, xpos + width - 15,
177                                ypos, 17, height);
178
179         // Create the workarea pixmap
180         createPixmap(width - 15 - 2 * bw, height - 2 * bw);
181
182         // the free object
183         fl_set_object_geometry(work_area, xpos + bw, ypos + bw,
184                                width - 15 - 2 * bw,
185                                height - 2 * bw);
186
187         fl_unfreeze_all_forms();
188
189 }
190
191
192 void WorkArea::createPixmap(int width, int height)
193 {
194         static int cur_width = -1;
195         static int cur_height = -1;
196
197         if (cur_width == width && cur_height == height && workareapixmap)
198                 return;
199         
200         cur_width = width;
201         cur_height = height;
202
203         if (workareapixmap)
204                 XFreePixmap(fl_display, workareapixmap);
205
206         if (lyxerr.debugging(Debug::GUI))
207                 lyxerr << "Creating pixmap ("
208                        << width << 'x' << height << ")" << endl;
209         
210         workareapixmap = XCreatePixmap(fl_display,
211                                        RootWindow(fl_display, 0),
212                                        width,
213                                        height, 
214                                        fl_get_visual_depth());
215         if (lyxerr.debugging(Debug::GUI))
216                 lyxerr << "\tpixmap=" << workareapixmap << endl;
217 }
218
219
220 void WorkArea::greyOut() const
221 {
222         fl_winset(FL_ObjWin(work_area));
223         fl_rectangle(1, work_area->x, work_area->y,
224                      work_area->w, work_area->h, FL_GRAY63);
225 }
226
227
228 void WorkArea::setFocus() const
229 {
230         fl_set_focus_object(work_area->form, work_area);
231 }
232
233
234 void WorkArea::setScrollbar(double pos, double length_fraction) const
235 {
236         fl_set_scrollbar_value(scrollbar, pos);
237         fl_set_scrollbar_size(scrollbar, scrollbar->h * length_fraction);
238 }
239
240
241 void WorkArea::setScrollbarBounds(double l1, double l2) const
242 {
243         fl_set_scrollbar_bounds(scrollbar, l1, l2);
244 }
245
246
247 void WorkArea::setScrollbarIncrements(double inc) const
248 {
249         fl_set_scrollbar_increment(scrollbar, work_area->h - inc, inc);
250 }
251
252
253 // Callback for scrollbar slider
254 void WorkArea::scroll_cb(FL_OBJECT * ob, long)
255 {
256         WorkArea * area = static_cast<WorkArea*>(ob->u_vdata);
257         // If we really want the accellerating scroll we can do that
258         // from here. IMHO that is a waste of effort since we already
259         // have other ways to move fast around in the document. (Lgb)
260         area->owner_->scrollCB(fl_get_scrollbar_value(ob));
261         waitForX();
262 }
263
264 bool Lgb_bug_find_hack = false;
265
266 int WorkArea::work_area_handler(FL_OBJECT * ob, int event,
267                                 FL_Coord, FL_Coord ,
268                                 int key, void * xev)
269 {
270         static int x_old = -1;
271         static int y_old = -1;
272         static long scrollbar_value_old = -1;
273         
274         XEvent * ev = static_cast<XEvent*>(xev);
275         WorkArea * area = static_cast<WorkArea*>(ob->u_vdata);
276
277         if (!area) return 1;
278         
279         switch (event){
280         case FL_DRAW:
281                 if (!area->work_area ||
282                     !area->work_area->form->visible)
283                         return 1;
284                 lyxerr[Debug::GUI] << "Workarea event: DRAW" << endl;
285                 area->createPixmap(area->workWidth(), area->height());
286                 Lgb_bug_find_hack = true;
287                 area->workAreaExpose();
288                 Lgb_bug_find_hack = false;
289                 break;
290         case FL_PUSH:
291                 if (!ev) break;
292                 // Should really have used xbutton.state
293                 lyxerr[Debug::GUI] << "Workarea event: PUSH" << endl;
294                 area->workAreaButtonPress(ev->xbutton.x - ob->x,
295                                           ev->xbutton.y - ob->y,
296                                           ev->xbutton.button);
297                 //area->workAreaKeyPress(XK_Pointer_Button1, ev->xbutton.state);
298                 break; 
299         case FL_RELEASE:
300                 if (!ev) break;
301                 // Should really have used xbutton.state
302                 lyxerr[Debug::GUI] << "Workarea event: RELEASE" << endl;
303                 area->workAreaButtonRelease(ev->xbutton.x - ob->x,
304                                       ev->xbutton.y - ob->y,
305                                       ev->xbutton.button);
306                 break;
307         case FL_MOUSE:
308                 if (!ev || ! area->scrollbar) break;
309                 if (ev->xmotion.x != x_old ||
310                     ev->xmotion.y != y_old ||
311                     fl_get_scrollbar_value(area->scrollbar) != scrollbar_value_old
312                         ) {
313                         lyxerr[Debug::GUI] << "Workarea event: MOUSE" << endl;
314                         area->workAreaMotionNotify(ev->xmotion.x - ob->x,
315                                              ev->xmotion.y - ob->y,
316                                              ev->xbutton.state);
317                 }
318                 break;
319         case FL_KEYBOARD:
320         {
321                 lyxerr[Debug::KEY] << "Workarea event: KEYBOARD";
322                 if (static_cast<XEvent*>(ev)->type == KeyPress)
323                         lyxerr << "KeyPress" << endl;
324                 else
325                         lyxerr << "KeyRelease" << endl;
326                 
327                 KeySym keysym = 0;
328                 char s_r[10];
329                 XKeyEvent * xke = reinterpret_cast<XKeyEvent *>(ev);
330                 XLookupString(xke, s_r, 10, &keysym, 0);
331                 if (lyxerr.debugging(Debug::KEY)) {
332                         char const * tmp = XKeysymToString(key);
333                         char const * tmp2 = XKeysymToString(keysym);
334                         string stm = (tmp ? tmp : "");
335                         string stm2 = (tmp2 ? tmp2 : "");
336                         
337                         lyxerr << "WorkArea: Key is `" << stm << "' ["
338                                << key << "]" << endl;
339                         lyxerr << "WorkArea: Keysym is `" << stm2 << "' ["
340                                << keysym << "]" << endl;
341                 }
342
343                 if (!key) break;
344                 
345                 KeySym ret_key = (keysym ? keysym : key);
346                 unsigned int ret_state = xke->state;
347                 
348                 static Time last_time_pressed = 0;
349                 static unsigned int last_key_pressed = 0;
350                 static unsigned int last_state_pressed = 0;
351                 if (xke->time - last_time_pressed < 50 // should perhaps be tunable
352                     && xke->state == last_state_pressed
353                     && xke->keycode == last_key_pressed) {
354                         lyxerr[Debug::KEY]
355                                 << "Workarea: Purging X events." << endl;
356                         XSync(fl_get_display(), 1);
357                         // This purge make f.ex. scrolling stop immidiatly when
358                         // releasing the PageDown button. The question is if
359                         // this purging of XEvents can cause any harm...
360                         // after some testing I can see no problems, but
361                         // I'd like other reports too.
362                         break;
363                 }
364                 last_time_pressed = xke->time;
365                 last_key_pressed = xke->keycode;
366                 last_state_pressed = xke->state;
367                 
368                 area->workAreaKeyPress(ret_key, ret_state);
369         }
370         break;
371         case FL_FOCUS:
372                 lyxerr[Debug::GUI] << "Workarea event: FOCUS" << endl;
373                 area->workAreaFocus();
374                 break;
375         case FL_UNFOCUS:
376                 lyxerr[Debug::GUI] << "Workarea event: UNFOCUS" << endl;
377                 area->workAreaUnfocus();
378                 break;
379         case FL_ENTER:
380                 lyxerr[Debug::GUI] << "Workarea event: ENTER" << endl;
381                 area->workAreaEnter();
382                 break;
383         case FL_LEAVE:
384                 lyxerr[Debug::GUI] << "Workarea event: LEAVE" << endl;
385                 area->workAreaLeave();
386                 break;
387         case FL_DBLCLICK:
388                 if (!ev) break;
389                 lyxerr[Debug::GUI] << "Workarea event: DBLCLICK" << endl;
390                 area->workAreaDoubleClick(ev->xbutton.x - ob->x,
391                                           ev->xbutton.y - ob->y,
392                                           ev->xbutton.button);
393                 break;
394         case FL_TRPLCLICK:
395                 if (!ev) break;
396                 lyxerr[Debug::GUI] << "Workarea event: TRPLCLICK" << endl;
397                 area->workAreaTripleClick(ev->xbutton.x - ob->x,
398                                           ev->xbutton.y - ob->y,
399                                           ev->xbutton.button);
400                 break;
401         case FL_OTHER:
402                 if (!ev) break;
403                         lyxerr[Debug::GUI] << "Workarea event: OTHER" << endl;
404
405                 break;
406         }
407   
408         return 1;
409 }
410
411
412 static string clipboard_selection;
413 static bool clipboard_read = false;
414
415 extern "C" {
416         static
417 int request_clipboard_cb(FL_OBJECT * /*ob*/, long /*type*/,
418                         void const * data, long size) 
419 {
420         clipboard_selection.erase();
421
422         if (size > 0)
423                 clipboard_selection.reserve(size);
424         for (int i = 0; i < size; ++i)
425                 clipboard_selection += static_cast<char const *>(data)[i];
426         clipboard_read = true;
427         return 0;
428 }
429 }
430
431 string WorkArea::getClipboard() const 
432 {
433         clipboard_read = false;
434         
435         if (fl_request_clipboard(work_area, 0, request_clipboard_cb) == -1)
436                 return string();
437
438         XEvent ev;
439         
440         while (!clipboard_read) {
441                 if (fl_check_forms() == FL_EVENT) {
442                         lyxerr << "LyX: This shouldn't happen..." << endl;
443                         fl_XNextEvent(&ev);
444                 }
445         }
446         return clipboard_selection;
447 }
448
449         
450 void WorkArea::putClipboard(string const & s) const
451 {
452         static string hold;
453         hold = s;
454         
455         fl_stuff_clipboard(work_area, 0, hold.c_str(), hold.size(), 0);
456 }