]> git.lyx.org Git - lyx.git/blob - src/frontends/gtk/GWorkArea.C
make it compile again
[lyx.git] / src / frontends / gtk / GWorkArea.C
1 /**
2  * \file GWorkArea.C
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Huang Ying
7  *
8  * Full author contact details are available in file CREDITS.
9  */
10
11 #include <config.h>
12
13 // Too hard to make concept checks work with this file
14 #ifdef _GLIBCXX_CONCEPT_CHECKS
15 #undef _GLIBCXX_CONCEPT_CHECKS
16 #endif
17 #ifdef _GLIBCPP_CONCEPT_CHECKS
18 #undef _GLIBCPP_CONCEPT_CHECKS
19 #endif
20
21 #include "GWorkArea.h"
22 #include "GView.h"
23 #include "GtkmmX.h"
24 #include "GLyXKeySym.h"
25
26 #include "BufferView.h"
27 #include "debug.h"
28 #include "funcrequest.h"
29 #include "LColor.h"
30 #include "WorkArea.h"
31 #include "Gui.h"
32 #include "Selection.h"
33
34 using boost::shared_ptr;
35
36 using std::string;
37
38 namespace lyx {
39 namespace frontend {
40
41 namespace {
42
43 mouse_button::state gButtonToLyx(guint gdkbutton)
44 {
45         // GDK uses int 1,2,3 but lyx uses enums (1,2,4)
46         switch (gdkbutton) {
47         case 1:
48                 return mouse_button::button1;
49         case 2:
50                 return mouse_button::button2;
51         case 3:
52                 return mouse_button::button3;
53         case 4:
54                 return mouse_button::button4;
55         case 5:
56                 return mouse_button::button5;
57         }
58
59         // This shouldn't happen, according to gdk docs
60         lyxerr << "gButtonToLyx: unhandled button index\n";
61         return mouse_button::button1;
62 }
63
64 } // namespace anon
65
66 ColorCache colorCache;
67
68 Gdk::Color * ColorCache::getColor(LColor_color clr)
69 {
70         MapIt it = cache_.find(clr);
71         return it == cache_.end() ? 0 : it->second.get();
72 }
73
74
75 XftColor * ColorCache::getXftColor(LColor_color clr)
76 {
77         MapIt2 it = cache2_.find(clr);
78         return it == cache2_.end() ? 0 : it->second.get();
79 }
80
81
82 void ColorCache::cacheColor(LColor_color clr, Gdk::Color * gclr)
83 {
84         cache_[clr] = shared_ptr<Gdk::Color>(gclr);
85 }
86
87
88 void ColorCache::cacheXftColor(LColor_color clr, XftColor * xclr)
89 {
90         cache2_[clr] = shared_ptr<XftColor>(xclr);
91 }
92
93
94 void ColorCache::clear()
95 {
96         cache_.clear();
97         cache2_.clear();
98 }
99
100
101 XftColor * ColorHandler::getXftColor(LColor_color clr)
102 {
103         XftColor * xclr = colorCache.getXftColor(clr);
104         if (!xclr) {
105                 xclr = new XftColor;
106                 Colormap colormap = GDK_COLORMAP_XCOLORMAP(
107                         owner_.getColormap()->gobj());
108                 Visual * visual = GDK_VISUAL_XVISUAL(
109                         owner_.getColormap()->get_visual()->gobj());
110                 XftColorAllocName(owner_.getDisplay(), visual, colormap,
111                                   const_cast<char*>(
112                                           lcolor.getX11Name(clr).c_str())
113                                   , xclr);
114                 colorCache.cacheXftColor(clr, xclr);
115         }
116         return xclr;
117 }
118
119
120 Gdk::Color * ColorHandler::getGdkColor(LColor_color clr)
121 {
122         Gdk::Color * gclr = colorCache.getColor(clr);
123         if (!gclr) {
124                 gclr = new Gdk::Color;
125                 gclr->parse(lcolor.getX11Name(clr));
126                 owner_.getColormap()->alloc_color(*gclr);
127                 colorCache.cacheColor(clr, gclr);
128         }
129         return gclr;
130 }
131
132
133 namespace
134 {
135
136
137 mouse_button::state gtkButtonState(unsigned int state)
138 {
139         mouse_button::state b = mouse_button::none;
140         if (state & GDK_BUTTON1_MASK)
141                 b = mouse_button::button1;
142         else if (state & GDK_BUTTON2_MASK)
143                 b = mouse_button::button2;
144         else if (state & GDK_BUTTON3_MASK)
145                 b = mouse_button::button3;
146         else if (state & GDK_BUTTON3_MASK)
147                 b = mouse_button::button3;
148         else if (state & GDK_BUTTON4_MASK)
149                 b = mouse_button::button4;
150         else if (state & GDK_BUTTON5_MASK)
151                 b = mouse_button::button5;
152         return b;
153 }
154
155
156 key_modifier::state gtkKeyState(guint state)
157 {
158         key_modifier::state k = key_modifier::none;
159         if (state & GDK_CONTROL_MASK)
160                 k |= key_modifier::ctrl;
161         if (state & GDK_SHIFT_MASK)
162                 k |= key_modifier::shift;
163         if (state & GDK_MOD1_MASK)
164                 k |= key_modifier::alt;
165         return k;
166 }
167
168
169 void inputCommitRelay(GtkIMContext */*imcontext*/, gchar * str, GWorkArea * area)
170 {
171         area->inputCommit(str);
172 }
173
174
175 }
176
177
178 GWorkArea::GWorkArea(LyXView & owner, int width, int height)
179     : view_(owner), workAreaPixmap_(0), painter_(*this), draw_(0), colorHandler_(*this),
180           adjusting_(false)
181 {
182         workArea_.set_size_request(width, height);
183         workArea_.set_double_buffered(false);
184         workArea_.add_events(Gdk::STRUCTURE_MASK | Gdk::EXPOSURE_MASK |
185                              Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK |
186                              Gdk::KEY_PRESS_MASK | Gdk::BUTTON1_MOTION_MASK);
187         workArea_.signal_expose_event().connect(
188                 sigc::mem_fun(*this, &GWorkArea::onExpose));
189         workArea_.signal_configure_event().connect(
190                 sigc::mem_fun(*this, &GWorkArea::onConfigure));
191         workArea_.signal_button_press_event().connect(
192                 sigc::mem_fun(*this, &GWorkArea::onButtonPress));
193         workArea_.signal_button_release_event().connect(
194                 sigc::mem_fun(*this, &GWorkArea::onButtonRelease));
195         workArea_.signal_key_press_event().connect(
196                 sigc::mem_fun(*this, &GWorkArea::onKeyPress));
197         workArea_.signal_motion_notify_event().connect(
198                 sigc::mem_fun(*this, &GWorkArea::onMotionNotify));
199         workArea_.show();
200         vscrollbar_.get_adjustment()->signal_value_changed().connect(
201                 sigc::mem_fun(*this, &GWorkArea::onScroll));
202         workArea_.signal_scroll_event().connect(
203                 sigc::mem_fun(*this, &GWorkArea::onScrollWheel));
204         vscrollbar_.show();
205         hbox_.children().push_back(Gtk::Box_Helpers::Element(workArea_));
206         hbox_.children().push_back(
207                 Gtk::Box_Helpers::Element(vscrollbar_,Gtk::PACK_SHRINK));
208         hbox_.show();
209
210         GView & gview = static_cast<GView &>(owner);
211         gview.getBox(GView::Center).children().push_back(
212                 Gtk::Box_Helpers::Element(hbox_));
213
214         workArea_.set_flags(workArea_.get_flags() | Gtk::CAN_DEFAULT |
215                             Gtk::CAN_FOCUS);
216         workArea_.grab_default();
217         gview.setGWorkArea(&workArea_);
218         imContext_ = GTK_IM_CONTEXT(gtk_im_multicontext_new());
219         g_signal_connect(G_OBJECT(imContext_), "commit",
220                          G_CALLBACK(&inputCommitRelay),
221                          this);
222 }
223
224
225 GWorkArea::~GWorkArea()
226 {
227         g_object_unref(imContext_);
228 }
229
230
231 Painter & GWorkArea::getPainter()
232 {
233         return painter_;
234 }
235
236
237 int GWorkArea::workWidth() const
238 {
239         return workArea_.get_width();
240 }
241
242
243 int GWorkArea::workHeight() const
244 {
245         return workArea_.get_height();
246 }
247
248
249 int GWorkArea::xpos() const
250 {
251         return 0;
252 }
253
254
255 int GWorkArea::ypos() const
256 {
257         return 0;
258 }
259
260
261 Glib::RefPtr<Gdk::Window> GWorkArea::getWindow()
262 {
263         return workArea_.get_window();
264 }
265
266
267 Display * GWorkArea::getDisplay() const
268 {
269         return GDK_WINDOW_XDISPLAY(
270                 const_cast<GdkWindow*>(workArea_.get_window()->gobj()));
271 }
272
273
274 Glib::RefPtr<Gdk::Pixmap> GWorkArea::getPixmap()
275 {
276         return workAreaPixmap_;
277 }
278
279
280 Glib::RefPtr<Gdk::GC> GWorkArea::getGC()
281 {
282         return workAreaGC_;
283 }
284
285
286 Glib::RefPtr<Gdk::Colormap> GWorkArea::getColormap()
287 {
288         return workArea_.get_colormap();
289 }
290
291
292 XftDraw * GWorkArea::getXftDraw()
293 {
294         return draw_;
295 }
296
297
298 ColorHandler & GWorkArea::getColorHandler()
299 {
300         return colorHandler_;
301 }
302
303
304 bool GWorkArea::onExpose(GdkEventExpose * event)
305 {
306         workArea_.get_window()->draw_drawable(
307                 workArea_.get_style()->get_black_gc(),
308                 workAreaPixmap_,
309                 event->area.x, event->area.y,
310                 event->area.x, event->area.y,
311                 event->area.width, event->area.height);
312         return true;
313 }
314
315
316 bool GWorkArea::onConfigure(GdkEventConfigure * /*event*/)
317 {
318         int x, y, width, height, depth;
319         workArea_.get_window()->get_geometry(x, y, width, height, depth);
320         if (draw_)
321                 XftDrawDestroy(draw_);
322         workAreaPixmap_ = Gdk::Pixmap::create(workArea_.get_window(),
323                                               width, height, depth);
324         Pixmap pixmap = GDK_PIXMAP_XID(workAreaPixmap_->gobj());
325         Colormap colormap = GDK_COLORMAP_XCOLORMAP(
326                 workArea_.get_colormap()->gobj());
327         Visual * visual = GDK_VISUAL_XVISUAL(
328                 workArea_.get_colormap()->get_visual()->gobj());
329         draw_ = XftDrawCreate(getDisplay(), pixmap,
330                               visual, colormap);
331         if (!workAreaGC_) {
332                 workAreaGC_ = Gdk::GC::create(workArea_.get_window());
333                 Gdk::Cursor cursor(Gdk::XTERM);
334                 workArea_.get_window()->set_cursor(cursor);
335                 gtk_im_context_set_client_window(
336                         imContext_, workArea_.get_window()->gobj());
337         }
338         view_.workArea()->resizeBufferView();
339         return true;
340 }
341
342
343 void GWorkArea::setScrollbarParams(int height, int pos, int line_height)
344 {
345         if (adjusting_)
346                 return;
347
348         adjusting_ = true;
349
350         Gtk::Adjustment * adjustment = vscrollbar_.get_adjustment();
351         adjustment->set_lower(0);
352         int workAreaHeight = workHeight();
353         if (!height || height < workAreaHeight) {
354                 adjustment->set_upper(workAreaHeight);
355                 adjustment->set_page_size(workAreaHeight);
356                 adjustment->set_value(0);
357                 adjustment->changed();
358                 adjusting_ = false;
359                 return;
360         }
361         adjustment->set_step_increment(line_height * 3);
362         adjustment->set_page_increment(workAreaHeight - line_height);
363         // Allow the user half a screen of blank at the end
364         // to make scrollbar consistant with centering the cursor
365         adjustment->set_upper(height + workAreaHeight / 4);
366         adjustment->set_page_size(workAreaHeight);
367         adjustment->set_value(pos);
368         adjustment->changed();
369         adjusting_ = false;
370 }
371
372
373 void GWorkArea::onScroll()
374 {
375         if (adjusting_)
376                 return;
377
378         adjusting_ = true;
379
380         double val = vscrollbar_.get_adjustment()->get_value();
381         view_.workArea()->scrollBufferView(static_cast<int>(val));
382         adjusting_ = false;
383 }
384
385
386 bool GWorkArea::onScrollWheel(GdkEventScroll * event)
387 {
388         Gtk::Adjustment * adjustment = vscrollbar_.get_adjustment();
389
390         double step;
391         if (event->state & GDK_CONTROL_MASK)
392                 step = adjustment->get_page_increment();
393         else
394                 step = adjustment->get_step_increment();
395
396         if (event->direction == GDK_SCROLL_UP)
397                 step *= -1.0f;
398
399         double target = adjustment->get_value() + step;
400         // Prevent the user getting a whole screen of blank when they
401         // try to scroll past the end of the doc
402         double max = adjustment->get_upper() - workHeight();
403         if (target > max)
404                 target = max;
405
406         adjustment->set_value(target);
407         return true;
408 }
409
410
411 bool GWorkArea::onButtonPress(GdkEventButton * event)
412 {
413         kb_action ka = LFUN_MOUSE_PRESS;
414         switch (event->type) {
415         case GDK_BUTTON_PRESS:
416                 ka = LFUN_MOUSE_PRESS;
417                 break;
418         case GDK_2BUTTON_PRESS:
419                 ka = LFUN_MOUSE_DOUBLE;
420                 break;
421         case GDK_3BUTTON_PRESS:
422                 ka = LFUN_MOUSE_TRIPLE;
423                 break;
424         default:
425                 break;
426         }
427         view_.workArea()->dispatch(FuncRequest(ka,
428                              static_cast<int>(event->x),
429                              static_cast<int>(event->y),
430                              gButtonToLyx(event->button)));
431         workArea_.grab_focus();
432         return true;
433 }
434
435
436 bool GWorkArea::onButtonRelease(GdkEventButton * event)
437 {
438         view_.workArea()->dispatch(FuncRequest(LFUN_MOUSE_RELEASE,
439                              static_cast<int>(event->x),
440                              static_cast<int>(event->y),
441                              gButtonToLyx(event->button)));
442         return true;
443 }
444
445
446 bool GWorkArea::onMotionNotify(GdkEventMotion * event)
447 {
448         static guint32 timeBefore;
449         Gtk::Adjustment * adjustment = vscrollbar_.get_adjustment();
450         double step = adjustment->get_step_increment();
451         double value = adjustment->get_value();
452         if (event->x < 0)
453                 value -= step;
454         else if (event->x > workArea_.get_height())
455                 value += step;
456         if (value != adjustment->get_value()) {
457                 if (event->time - timeBefore > 200) {
458                         adjustment->set_value(value);
459                         adjustment->value_changed();
460                 }
461                 timeBefore = event->time;
462         }
463         view_.workArea()->dispatch(FuncRequest(LFUN_MOUSE_MOTION,
464                              static_cast<int>(event->x),
465                              static_cast<int>(event->y),
466                              gtkButtonState(event->state)));
467         return true;
468 }
469
470
471 void GWorkArea::inputCommit(gchar * str)
472 {
473         inputCache_ = Glib::locale_from_utf8(str);
474 }
475
476
477 bool GWorkArea::onKeyPress(GdkEventKey * event)
478 {
479 // #ifdef I18N
480 //      inputCache_ = "";
481 //      bool inputGet = gtk_im_context_filter_keypress(imContext_, event);
482 //      // cope with ascii
483 //      if ((inputGet && inputCache_.size() == 1 && inputCache_[0] < 128) ||
484 //          !inputGet) {
485 // #endif
486                 GLyXKeySym *glk = new GLyXKeySym(event->keyval);
487                 view_.workArea()->processKeySym(LyXKeySymPtr(glk),
488                                             gtkKeyState(event->state));
489 // #ifdef I18N
490 //      } else if (!inputCache_.empty())
491 //              workAreaCJK_IMprocess(inputCache_.size(), inputCache_.data());
492 // #endif
493         return true;
494 }
495
496 } // namespace frontend
497 } // namespace lyx