]> git.lyx.org Git - features.git/blob - src/frontends/qt4/GuiWorkArea.cpp
Move Color::color enum to ColorCode.h
[features.git] / src / frontends / qt4 / GuiWorkArea.cpp
1 /**
2  * \file GuiWorkArea.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author John Levon
7  * \author Abdelrazak Younes
8  *
9  * Full author contact details are available in file CREDITS.
10  */
11
12 #include <config.h>
13
14 #include "GuiWorkArea.h"
15
16 #include "GuiApplication.h"
17 #include "GuiPainter.h"
18 #include "GuiKeySymbol.h"
19 #include "qt_helpers.h"
20
21 #include "frontends/LyXView.h"
22
23 #include "Buffer.h"
24 #include "BufferView.h"
25 #include "Cursor.h"
26 #include "debug.h"
27 #include "FuncRequest.h"
28 #include "LyXFunc.h"
29 #include "LyXRC.h"
30 #include "version.h"
31
32 #include "graphics/GraphicsImage.h"
33 #include "graphics/GraphicsLoader.h"
34
35 #include <QInputContext>
36 #include <QLayout>
37 #include <QMainWindow>
38 #include <QPainter>
39 #include <QToolButton>
40 #include <QPalette>
41 #include <QScrollBar>
42 #include <QTabBar>
43 #include <QTimer>
44
45 #include <boost/current_function.hpp>
46 #include <boost/bind.hpp>
47
48 #ifdef Q_WS_X11
49 #include <QX11Info>
50 extern "C" int XEventsQueued(Display *display, int mode);
51 #endif
52
53 #ifdef Q_WS_WIN
54 int const CursorWidth = 2;
55 #else
56 int const CursorWidth = 1;
57 #endif
58
59 #undef KeyPress
60 #undef NoModifier 
61
62 using std::endl;
63 using std::string;
64
65 namespace lyx {
66
67 using support::FileName;
68
69 /// return the LyX mouse button state from Qt's
70 static mouse_button::state q_button_state(Qt::MouseButton button)
71 {
72         mouse_button::state b = mouse_button::none;
73         switch (button) {
74                 case Qt::LeftButton:
75                         b = mouse_button::button1;
76                         break;
77                 case Qt::MidButton:
78                         b = mouse_button::button2;
79                         break;
80                 case Qt::RightButton:
81                         b = mouse_button::button3;
82                         break;
83                 default:
84                         break;
85         }
86         return b;
87 }
88
89
90 /// return the LyX mouse button state from Qt's
91 mouse_button::state q_motion_state(Qt::MouseButtons state)
92 {
93         mouse_button::state b = mouse_button::none;
94         if (state & Qt::LeftButton)
95                 b |= mouse_button::button1;
96         if (state & Qt::MidButton)
97                 b |= mouse_button::button2;
98         if (state & Qt::RightButton)
99                 b |= mouse_button::button3;
100         return b;
101 }
102
103
104 namespace frontend {
105
106 class CursorWidget {
107 public:
108         CursorWidget() {}
109
110         void draw(QPainter & painter)
111         {
112                 if (show_ && rect_.isValid()) {
113                         switch (shape_) {
114                         case L_SHAPE:
115                                 painter.fillRect(rect_.x(), rect_.y(), CursorWidth, rect_.height(), color_);
116                                 painter.setPen(color_);
117                                 painter.drawLine(rect_.bottomLeft().x() + CursorWidth, rect_.bottomLeft().y(),
118                                                                                                  rect_.bottomRight().x(), rect_.bottomLeft().y());
119                                 break;
120                         
121                         case REVERSED_L_SHAPE:
122                                 painter.fillRect(rect_.x() + rect_.height() / 3, rect_.y(), CursorWidth, rect_.height(), color_);
123                                 painter.setPen(color_);
124                                 painter.drawLine(rect_.bottomRight().x() - CursorWidth, rect_.bottomLeft().y(),
125                                                                                                          rect_.bottomLeft().x(), rect_.bottomLeft().y());
126                                 break;
127                                         
128                         default:
129                                 painter.fillRect(rect_, color_);
130                                 break;
131                         }
132                 }
133         }
134
135         void update(int x, int y, int h, CursorShape shape)
136         {
137                 color_ = guiApp->colorCache().get(Color_cursor);
138                 shape_ = shape;
139                 switch (shape) {
140                 case L_SHAPE:
141                         rect_ = QRect(x, y, CursorWidth + h / 3, h);
142                         break;
143                 case REVERSED_L_SHAPE:
144                         rect_ = QRect(x - h / 3, y, CursorWidth + h / 3, h);
145                         break;
146                 default: 
147                         rect_ = QRect(x, y, CursorWidth, h);
148                         break;
149                 }
150         }
151
152         void show(bool set_show = true) { show_ = set_show; }
153         void hide() { show_ = false; }
154
155         QRect const & rect() { return rect_; }
156
157 private:
158         ///
159         CursorShape shape_;
160         ///
161         bool show_;
162         ///
163         QColor color_;
164         ///
165         QRect rect_;
166 };
167
168
169 // This is a 'heartbeat' generating synthetic mouse move events when the
170 // cursor is at the top or bottom edge of the viewport. One scroll per 0.2 s
171 SyntheticMouseEvent::SyntheticMouseEvent()
172         : timeout(200), restart_timeout(true),
173           x_old(-1), y_old(-1), scrollbar_value_old(-1.0)
174 {}
175
176
177 GuiWorkArea::GuiWorkArea(Buffer & buf, LyXView & lv)
178         : WorkArea(buf, lv), need_resize_(false), schedule_redraw_(false),
179           preedit_lines_(1)
180 {
181         screen_ = QPixmap(viewport()->width(), viewport()->height());
182         cursor_ = new frontend::CursorWidget();
183         cursor_->hide();
184
185         setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
186         setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
187         setAcceptDrops(true);
188         setMouseTracking(true);
189         setMinimumSize(100, 70);
190
191         viewport()->setAutoFillBackground(false);
192         // We don't need double-buffering nor SystemBackground on
193         // the viewport because we have our own backing pixmap.
194         viewport()->setAttribute(Qt::WA_NoSystemBackground);
195
196         setFocusPolicy(Qt::WheelFocus);
197
198         viewport()->setCursor(Qt::IBeamCursor);
199
200         synthetic_mouse_event_.timeout.timeout.connect(
201                 boost::bind(&GuiWorkArea::generateSyntheticMouseEvent,
202                             this));
203
204         // Initialize the vertical Scroll Bar
205         QObject::connect(verticalScrollBar(), SIGNAL(actionTriggered(int)),
206                 this, SLOT(adjustViewWithScrollBar(int)));
207
208         // disable context menu for the scrollbar
209         verticalScrollBar()->setContextMenuPolicy(Qt::NoContextMenu);
210
211         // PageStep only depends on the viewport height.
212         verticalScrollBar()->setPageStep(viewport()->height());
213
214         LYXERR(Debug::GUI) << BOOST_CURRENT_FUNCTION
215                 << "\n Area width\t" << width()
216                 << "\n Area height\t" << height()
217                 << "\n viewport width\t" << viewport()->width()
218                 << "\n viewport height\t" << viewport()->height()
219                 << endl;
220
221         // Enables input methods for asian languages.
222         // Must be set when creating custom text editing widgets.
223         setAttribute(Qt::WA_InputMethodEnabled, true);
224 }
225
226
227 void GuiWorkArea::setScrollbarParams(int h, int scroll_pos, int scroll_line_step)
228 {
229         if (verticalScrollBarPolicy() != Qt::ScrollBarAlwaysOn)
230                 setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
231
232         verticalScrollBar()->setTracking(false);
233
234         // do what cursor movement does (some grey)
235         h += height() / 4;
236         int scroll_max_ = std::max(0, h - height());
237
238         verticalScrollBar()->setRange(0, scroll_max_);
239         verticalScrollBar()->setSliderPosition(scroll_pos);
240         verticalScrollBar()->setSingleStep(scroll_line_step);
241         verticalScrollBar()->setValue(scroll_pos);
242
243         verticalScrollBar()->setTracking(true);
244 }
245
246
247 void GuiWorkArea::adjustViewWithScrollBar(int action)
248 {
249         stopBlinkingCursor();
250         if (action == QAbstractSlider::SliderPageStepAdd)
251                 buffer_view_->scrollDown(viewport()->height());
252         else if (action == QAbstractSlider::SliderPageStepSub)
253                 buffer_view_->scrollUp(viewport()->height());
254         else
255                 buffer_view_->scrollDocView(verticalScrollBar()->sliderPosition());
256
257         if (lyxrc.cursor_follows_scrollbar) {
258                 buffer_view_->setCursorFromScrollbar();
259                 lyx_view_->updateLayoutChoice();
260         }
261         // Show the cursor immediately after any operation.
262         startBlinkingCursor();
263         QApplication::syncX();
264 }
265
266
267 void GuiWorkArea::focusInEvent(QFocusEvent * /*event*/)
268 {
269         // Repaint the whole screen.
270         // Note: this is different from redraw() as only the backing pixmap
271         // will be redrawn, which is cheap.
272         viewport()->repaint();
273
274         startBlinkingCursor();
275 }
276
277
278 void GuiWorkArea::focusOutEvent(QFocusEvent * /*event*/)
279 {
280         stopBlinkingCursor();
281 }
282
283
284 void GuiWorkArea::mousePressEvent(QMouseEvent * e)
285 {
286         if (dc_event_.active && dc_event_ == *e) {
287                 dc_event_.active = false;
288                 FuncRequest cmd(LFUN_MOUSE_TRIPLE, e->x(), e->y(),
289                         q_button_state(e->button()));
290                 dispatch(cmd);
291                 return;
292         }
293
294         inputContext()->reset();
295
296         FuncRequest const cmd(LFUN_MOUSE_PRESS, e->x(), e->y(),
297                 q_button_state(e->button()));
298         dispatch(cmd, q_key_state(e->modifiers()));
299 }
300
301
302 void GuiWorkArea::mouseReleaseEvent(QMouseEvent * e)
303 {
304         if (synthetic_mouse_event_.timeout.running())
305                 synthetic_mouse_event_.timeout.stop();
306
307         FuncRequest const cmd(LFUN_MOUSE_RELEASE, e->x(), e->y(),
308                               q_button_state(e->button()));
309         dispatch(cmd);
310 }
311
312
313 void GuiWorkArea::mouseMoveEvent(QMouseEvent * e)
314 {
315         // we kill the triple click if we move
316         doubleClickTimeout();
317         FuncRequest cmd(LFUN_MOUSE_MOTION, e->x(), e->y(),
318                               q_motion_state(e->buttons()));
319
320         // If we're above or below the work area...
321         if (e->y() <= 20 || e->y() >= viewport()->height() - 20) {
322                 // Make sure only a synthetic event can cause a page scroll,
323                 // so they come at a steady rate:
324                 if (e->y() <= 20)
325                         // _Force_ a scroll up:
326                         cmd.y = -40;
327                 else
328                         cmd.y = viewport()->height();
329                 // Store the event, to be handled when the timeout expires.
330                 synthetic_mouse_event_.cmd = cmd;
331
332                 if (synthetic_mouse_event_.timeout.running())
333                         // Discard the event. Note that it _may_ be handled
334                         // when the timeout expires if
335                         // synthetic_mouse_event_.cmd has not been overwritten.
336                         // Ie, when the timeout expires, we handle the
337                         // most recent event but discard all others that
338                         // occurred after the one used to start the timeout
339                         // in the first place.
340                         return;
341                 else {
342                         synthetic_mouse_event_.restart_timeout = true;
343                         synthetic_mouse_event_.timeout.start();
344                         // Fall through to handle this event...
345                 }
346
347         } else if (synthetic_mouse_event_.timeout.running()) {
348                 // Store the event, to be possibly handled when the timeout
349                 // expires.
350                 // Once the timeout has expired, normal control is returned
351                 // to mouseMoveEvent (restart_timeout = false).
352                 // This results in a much smoother 'feel' when moving the
353                 // mouse back into the work area.
354                 synthetic_mouse_event_.cmd = cmd;
355                 synthetic_mouse_event_.restart_timeout = false;
356                 return;
357         }
358
359         // Has anything changed on-screen since the last QMouseEvent
360         // was received?
361         double const scrollbar_value = verticalScrollBar()->value();
362         if (e->x() != synthetic_mouse_event_.x_old ||
363             e->y() != synthetic_mouse_event_.y_old ||
364             scrollbar_value != synthetic_mouse_event_.scrollbar_value_old) {
365                 // Yes it has. Store the params used to check this.
366                 synthetic_mouse_event_.x_old = e->x();
367                 synthetic_mouse_event_.y_old = e->y();
368                 synthetic_mouse_event_.scrollbar_value_old = scrollbar_value;
369
370                 // ... and dispatch the event to the LyX core.
371                 dispatch(cmd);
372         }
373 }
374
375
376 void GuiWorkArea::wheelEvent(QWheelEvent * e)
377 {
378         // Wheel rotation by one notch results in a delta() of 120 (see
379         // documentation of QWheelEvent)
380         int const lines = qApp->wheelScrollLines() * e->delta() / 120;
381         verticalScrollBar()->setValue(verticalScrollBar()->value() -
382                         lines *  verticalScrollBar()->singleStep());
383         adjustViewWithScrollBar();
384 }
385
386
387 void GuiWorkArea::generateSyntheticMouseEvent()
388 {
389 // Set things off to generate the _next_ 'pseudo' event.
390         if (synthetic_mouse_event_.restart_timeout)
391                 synthetic_mouse_event_.timeout.start();
392
393         // Has anything changed on-screen since the last timeout signal
394         // was received?
395         double const scrollbar_value = verticalScrollBar()->value();
396         if (scrollbar_value != synthetic_mouse_event_.scrollbar_value_old) {
397                 // Yes it has. Store the params used to check this.
398                 synthetic_mouse_event_.scrollbar_value_old = scrollbar_value;
399
400                 // ... and dispatch the event to the LyX core.
401                 dispatch(synthetic_mouse_event_.cmd);
402         }
403 }
404
405
406 void GuiWorkArea::keyPressEvent(QKeyEvent * ev)
407 {
408         // do nothing if there are other events
409         // (the auto repeated events come too fast)
410         // \todo FIXME: remove hard coded Qt keys, process the key binding
411 #ifdef Q_WS_X11
412         if (XEventsQueued(QX11Info::display(), 0) > 1 && ev->isAutoRepeat() 
413                         && (Qt::Key_PageDown || Qt::Key_PageUp)) {
414                 LYXERR(Debug::KEY)      
415                         << BOOST_CURRENT_FUNCTION << endl
416                         << "system is busy: scroll key event ignored" << endl;
417                 ev->ignore();
418                 return;
419         }
420 #endif
421
422         LYXERR(Debug::KEY) << BOOST_CURRENT_FUNCTION
423                 << " count=" << ev->count()
424                 << " text=" << fromqstr(ev->text())
425                 << " isAutoRepeat=" << ev->isAutoRepeat()
426                 << " key=" << ev->key()
427                 << endl;
428
429         KeySymbol sym;
430         setKeySymbol(&sym, ev);
431         processKeySym(sym, q_key_state(ev->modifiers()));
432 }
433
434
435 void GuiWorkArea::doubleClickTimeout()
436 {
437         dc_event_.active = false;
438 }
439
440
441 void GuiWorkArea::mouseDoubleClickEvent(QMouseEvent * ev)
442 {
443         dc_event_ = double_click(ev);
444         QTimer::singleShot(QApplication::doubleClickInterval(), this,
445                            SLOT(doubleClickTimeout()));
446         FuncRequest cmd(LFUN_MOUSE_DOUBLE,
447                         ev->x(), ev->y(),
448                         q_button_state(ev->button()));
449         dispatch(cmd);
450 }
451
452
453 void GuiWorkArea::resizeEvent(QResizeEvent * ev)
454 {
455         QAbstractScrollArea::resizeEvent(ev);
456         need_resize_ = true;
457 }
458
459
460 void GuiWorkArea::update(int x, int y, int w, int h)
461 {
462         viewport()->repaint(x, y, w, h);
463 }
464
465
466 void GuiWorkArea::paintEvent(QPaintEvent * ev)
467 {
468         QRect const rc = ev->rect();
469         /*
470         LYXERR(Debug::PAINTING) << "paintEvent begin: x: " << rc.x()
471                 << " y: " << rc.y()
472                 << " w: " << rc.width()
473                 << " h: " << rc.height() << endl;
474         */
475
476         if (need_resize_) {
477                 verticalScrollBar()->setPageStep(viewport()->height());
478                 screen_ = QPixmap(viewport()->width(), viewport()->height());
479                 resizeBufferView();
480                 updateScreen();
481                 WorkArea::hideCursor();
482                 WorkArea::showCursor();
483                 need_resize_ = false;
484         }
485
486         QPainter pain(viewport());
487         pain.drawPixmap(rc, screen_, rc);
488         cursor_->draw(pain);
489 }
490
491
492 void GuiWorkArea::expose(int x, int y, int w, int h)
493 {
494         updateScreen();
495         update(x, y, w, h);
496 }
497
498
499 void GuiWorkArea::updateScreen()
500 {
501         GuiPainter pain(&screen_);
502         verticalScrollBar()->show();
503         buffer_view_->draw(pain);
504 }
505
506
507 void GuiWorkArea::showCursor(int x, int y, int h, CursorShape shape)
508 {
509         if (schedule_redraw_) {
510                 buffer_view_->updateMetrics(false);
511                 updateScreen();
512                 viewport()->update(QRect(0, 0, viewport()->width(), viewport()->height()));
513                 schedule_redraw_ = false;
514                 // Show the cursor immediately after the update.
515                 hideCursor();
516                 toggleCursor();
517                 return;
518         }
519
520         cursor_->update(x, y, h, shape);
521         cursor_->show();
522         viewport()->update(cursor_->rect());
523 }
524
525
526 void GuiWorkArea::removeCursor()
527 {
528         cursor_->hide();
529         //if (!qApp->focusWidget())
530                 viewport()->update(cursor_->rect());
531 }
532
533
534 void GuiWorkArea::inputMethodEvent(QInputMethodEvent * e)
535 {
536         QString const & commit_string = e->commitString();
537         docstring const & preedit_string
538                 = qstring_to_ucs4(e->preeditString());
539
540         if (!commit_string.isEmpty()) {
541
542                 LYXERR(Debug::KEY) << BOOST_CURRENT_FUNCTION
543                         << " preeditString =" << fromqstr(e->preeditString())
544                         << " commitString  =" << fromqstr(e->commitString())
545                         << endl;
546
547                 int key = 0;
548
549                 // FIXME Iwami 04/01/07: we should take care also of UTF16 surrogates here.
550                 for (int i = 0; i < commit_string.size(); ++i) {
551                         QKeyEvent ev(QEvent::KeyPress, key, Qt::NoModifier, commit_string[i]);
552                         keyPressEvent(&ev);
553                 }
554         }
555
556         // Hide the cursor during the kana-kanji transformation.
557         if (preedit_string.empty())
558                 startBlinkingCursor();
559         else
560                 stopBlinkingCursor();
561
562         // last_width : for checking if last preedit string was/wasn't empty.
563         static bool last_width = false;
564         if (!last_width && preedit_string.empty()) {
565                 // if last_width is last length of preedit string.
566                 e->accept();
567                 return;
568         }
569
570         GuiPainter pain(&screen_);
571         buffer_view_->updateMetrics(false);
572         buffer_view_->draw(pain);
573         Font font = buffer_view_->cursor().getFont();
574         FontMetrics const & fm = theFontMetrics(font);
575         int height = fm.maxHeight();
576         int cur_x = cursor_->rect().left();
577         int cur_y = cursor_->rect().bottom();
578
579         // redraw area of preedit string.
580         update(0, cur_y - height, GuiWorkArea::width(),
581                 (height + 1) * preedit_lines_);
582
583         if (preedit_string.empty()) {
584                 last_width = false;
585                 preedit_lines_ = 1;
586                 e->accept();
587                 return;
588         }
589         last_width = true;
590
591         // att : stores an IM attribute.
592         QList<QInputMethodEvent::Attribute> const & att = e->attributes();
593
594         // get attributes of input method cursor.
595         // cursor_pos : cursor position in preedit string.
596         size_t cursor_pos = 0;
597         bool cursor_is_visible = false;
598         for (int i = 0; i < att.size(); ++i) {
599                 if (att.at(i).type == QInputMethodEvent::Cursor) {
600                         cursor_pos = att.at(i).start;
601                         cursor_is_visible = att.at(i).length != 0;
602                         break;
603                 }
604         }
605
606         size_t preedit_length = preedit_string.length();
607
608         // get position of selection in input method.
609         // FIXME: isn't there a way to do this simplier?
610         // rStart : cursor position in selected string in IM.
611         size_t rStart = 0;
612         // rLength : selected string length in IM.
613         size_t rLength = 0;
614         if (cursor_pos < preedit_length) {
615                 for (int i = 0; i < att.size(); ++i) {
616                         if (att.at(i).type == QInputMethodEvent::TextFormat) {
617                                 if (att.at(i).start <= int(cursor_pos)
618                                         && int(cursor_pos) < att.at(i).start + att.at(i).length) {
619                                                 rStart = att.at(i).start;
620                                                 rLength = att.at(i).length;
621                                                 if (!cursor_is_visible)
622                                                         cursor_pos += rLength;
623                                                 break;
624                                 }
625                         }
626                 }
627         }
628         else {
629                 rStart = cursor_pos;
630                 rLength = 0;
631         }
632
633         int const right_margin = rightMargin();
634         Painter::preedit_style ps;
635         // Most often there would be only one line:
636         preedit_lines_ = 1;
637         for (size_t pos = 0; pos != preedit_length; ++pos) {
638                 char_type const typed_char = preedit_string[pos];
639                 // reset preedit string style
640                 ps = Painter::preedit_default;
641
642                 // if we reached the right extremity of the screen, go to next line.
643                 if (cur_x + fm.width(typed_char) > GuiWorkArea::width() - right_margin) {
644                         cur_x = right_margin;
645                         cur_y += height + 1;
646                         ++preedit_lines_;
647                 }
648                 // preedit strings are displayed with dashed underline
649                 // and partial strings are displayed white on black indicating
650                 // that we are in selecting mode in the input method.
651                 // FIXME: rLength == preedit_length is not a changing condition
652                 // FIXME: should be put out of the loop.
653                 if (pos >= rStart
654                         && pos < rStart + rLength
655                         && !(cursor_pos < rLength && rLength == preedit_length))
656                         ps = Painter::preedit_selecting;
657
658                 if (pos == cursor_pos
659                         && (cursor_pos < rLength && rLength == preedit_length))
660                         ps = Painter::preedit_cursor;
661
662                 // draw one character and update cur_x.
663                 cur_x += pain.preeditText(cur_x, cur_y, typed_char, font, ps);
664         }
665
666         // update the preedit string screen area.
667         update(0, cur_y - preedit_lines_*height, GuiWorkArea::width(),
668                 (height + 1) * preedit_lines_);
669
670         // Don't forget to accept the event!
671         e->accept();
672 }
673
674
675 QVariant GuiWorkArea::inputMethodQuery(Qt::InputMethodQuery query) const
676 {
677         QRect cur_r(0,0,0,0);
678         switch (query) {
679                 // this is the CJK-specific composition window position.
680                 case Qt::ImMicroFocus:
681                         cur_r = cursor_->rect();
682                         if (preedit_lines_ != 1)
683                                 cur_r.moveLeft(10);
684                         cur_r.moveBottom(cur_r.bottom() + cur_r.height() * preedit_lines_);
685                         // return lower right of cursor in LyX.
686                         return cur_r;
687                 default:
688                         return QWidget::inputMethodQuery(query);
689         }
690 }
691
692
693 ////////////////////////////////////////////////////////////////////
694 //
695 // TabWorkArea 
696 //
697 ////////////////////////////////////////////////////////////////////
698
699 TabWorkArea::TabWorkArea(QWidget * parent): QTabWidget(parent)
700 {
701         QPalette pal = palette();
702         pal.setColor(QPalette::Active, QPalette::Button, pal.color(QPalette::Active, QPalette::Window));
703         pal.setColor(QPalette::Disabled, QPalette::Button, pal.color(QPalette::Disabled, QPalette::Window));
704         pal.setColor(QPalette::Inactive, QPalette::Button, pal.color(QPalette::Inactive, QPalette::Window));
705
706         QToolButton * closeTabButton = new QToolButton(this);
707     closeTabButton->setPalette(pal);
708         closeTabButton->setIcon(QIcon(":/images/closetab.png"));
709         closeTabButton->setText("Close");
710         closeTabButton->setAutoRaise(true);
711         closeTabButton->setCursor(Qt::ArrowCursor);
712         closeTabButton->setToolTip(tr("Close tab"));
713         closeTabButton->setEnabled(true);
714
715         QObject::connect(this, SIGNAL(currentChanged(int)),
716                 this, SLOT(on_currentTabChanged(int)));
717         QObject::connect(closeTabButton, SIGNAL(clicked()),
718                 this, SLOT(closeCurrentTab()));
719
720         setCornerWidget(closeTabButton);
721 #if QT_VERSION >= 0x040200
722         setUsesScrollButtons(true);
723 #endif
724 }
725
726
727 void TabWorkArea::showBar(bool show)
728 {
729         tabBar()->setVisible(show);
730 }
731
732
733 GuiWorkArea * TabWorkArea::currentWorkArea()
734 {
735         if (count() == 0)
736                 return 0;
737
738         GuiWorkArea * wa = dynamic_cast<GuiWorkArea *>(currentWidget()); 
739         BOOST_ASSERT(wa);
740         return wa;
741 }
742
743
744 GuiWorkArea * TabWorkArea::workArea(Buffer & buffer)
745 {
746         for (int i = 0; i != count(); ++i) {
747                 GuiWorkArea * wa = dynamic_cast<GuiWorkArea *>(widget(i));
748                 BOOST_ASSERT(wa);
749                 if (&wa->bufferView().buffer() == &buffer)
750                         return wa;
751         }
752         return 0;
753 }
754
755
756 void TabWorkArea::closeAll()
757 {
758         while (count()) {
759                 GuiWorkArea * wa = dynamic_cast<GuiWorkArea *>(widget(0));
760                 BOOST_ASSERT(wa);
761                 removeTab(0);
762                 delete wa;
763         }
764 }
765
766
767 bool TabWorkArea::setCurrentWorkArea(GuiWorkArea * work_area)
768 {
769         BOOST_ASSERT(work_area);
770         int index = indexOf(work_area);
771         if (index == -1)
772                 return false;
773
774         if (index == currentIndex())
775                 // Make sure the work area is up to date.
776                 on_currentTabChanged(index);
777         else
778                 // Switch to the work area.
779                 setCurrentIndex(index);
780         work_area->setFocus();
781
782         return true;
783 }
784
785
786 bool TabWorkArea::removeWorkArea(GuiWorkArea * work_area)
787 {
788         BOOST_ASSERT(work_area);
789         int index = indexOf(work_area);
790         if (index == -1)
791                 return false;
792
793         work_area->setUpdatesEnabled(false);
794         removeTab(index);
795         delete work_area;
796
797         if (count()) {
798                 // make sure the next work area is enabled.
799                 currentWidget()->setUpdatesEnabled(true);
800                 // Hide tabbar if there's only one tab.
801                 showBar(count() > 1);
802         }
803         return true;
804 }
805
806
807 void TabWorkArea::on_currentTabChanged(int i)
808 {
809         GuiWorkArea * wa = dynamic_cast<GuiWorkArea *>(widget(i));
810         BOOST_ASSERT(wa);
811         BufferView & bv = wa->bufferView();
812         bv.updateMetrics(false);
813         bv.cursor().fixIfBroken();
814         wa->setUpdatesEnabled(true);
815         wa->redraw();
816         wa->setFocus();
817         ///
818         currentWorkAreaChanged(wa);
819
820         LYXERR(Debug::GUI) << "currentTabChanged " << i
821                 << "File" << bv.buffer().absFileName() << endl;
822 }
823
824
825 void TabWorkArea::closeCurrentTab()
826 {
827         lyx::dispatch(FuncRequest(LFUN_BUFFER_CLOSE));
828 }
829
830 } // namespace frontend
831 } // namespace lyx
832
833 #include "GuiWorkArea_moc.cpp"