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