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