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