]> git.lyx.org Git - lyx.git/blob - src/frontends/qt4/GuiView.C
Document pasteParagraphList as hinted by Jean-Marc
[lyx.git] / src / frontends / qt4 / GuiView.C
1 /**
2  * \file GuiView.C
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Lars Gullik Bjønnes
7  * \author John Levon
8  * \author Abdelrazak Younes
9  *
10  * Full author contact details are available in file CREDITS.
11  */
12
13 #include <config.h>
14
15 #include "GuiView.h"
16
17 #include "GuiImplementation.h"
18 #include "GuiWorkArea.h"
19 #include "QLMenubar.h"
20 #include "QLToolbar.h"
21 #include "QCommandBuffer.h"
22 #include "qt_helpers.h"
23
24 #include "frontends/Application.h"
25 #include "frontends/Gui.h"
26 #include "frontends/WorkArea.h"
27
28 #include "support/filetools.h"
29 #include "support/convert.h"
30 #include "support/lstrings.h"
31
32 #include "BufferView.h"
33 #include "bufferlist.h"
34 #include "debug.h"
35 #include "funcrequest.h"
36 #include "lyx_cb.h"
37 #include "lyxrc.h"
38 #include "lyx_main.h"
39 #include "session.h"
40 #include "lyxfunc.h"
41 #include "MenuBackend.h"
42 #include "buffer.h"
43 #include "bufferlist.h"
44
45 #include <QAction>
46 #include <QApplication>
47 #include <QCloseEvent>
48 #include <QPixmap>
49 #include <QStatusBar>
50 #include <QToolBar>
51 #include <QTabBar>
52 #include <QDesktopWidget>
53 #include <QVBoxLayout>
54
55 #include <boost/bind.hpp>
56
57 using std::endl;
58 using std::string;
59 using std::vector;
60
61 using lyx::support::onlyFilename;
62
63 namespace lyx {
64
65 using support::subst;
66 using support::libFileSearch;
67
68 namespace frontend {
69
70 namespace {
71
72 int const statusbar_timer_value = 3000;
73
74 } // namespace anon
75
76
77 class WidgetWithTabBar : public QWidget
78 {
79 public:
80         QTabBar* tabbar;
81         WidgetWithTabBar(QWidget* w)
82         {
83                 tabbar = new QTabBar;
84                 QVBoxLayout* l = new QVBoxLayout;
85                 l->addWidget(tabbar);
86                 l->addWidget(w);
87                 l->setMargin(0);
88                 setLayout(l);
89         }
90 };
91
92 struct GuiView::GuiViewPrivate
93 {
94         typedef std::map<int, FuncRequest> FuncMap;
95         typedef std::pair<int, FuncRequest> FuncMapPair;
96         typedef std::map<string, QString> NameMap;
97         typedef std::pair<string, QString> NameMapPair;
98
99         FuncMap funcmap;
100         NameMap namemap;
101         WidgetWithTabBar* wt;
102
103         int posx_offset;
104         int posy_offset;
105
106         GuiViewPrivate() : wt(0), posx_offset(0), posy_offset(0)
107         {}
108 };
109
110 GuiView::GuiView(int id)
111         : QMainWindow(), LyXView(id), commandbuffer_(0), d(*new GuiViewPrivate)
112 {
113         setAttribute(Qt::WA_DeleteOnClose, true);
114         setAttribute(Qt::WA_QuitOnClose, true);
115
116 //      setToolButtonStyle(Qt::ToolButtonIconOnly);
117 //      setIconSize(QSize(12,12));
118
119 //      bufferview_.reset(new BufferView(this, width, height));
120
121 #ifndef Q_WS_MACX
122         //  assign an icon to main form. We do not do it under Qt/Mac,
123         //  since the icon is provided in the application bundle.
124         string const iconname = libFileSearch("images", "lyx", "xpm");
125         if (!iconname.empty())
126                 setWindowIcon(QPixmap(toqstr(iconname)));
127 #endif
128 }
129
130
131 GuiView::~GuiView()
132 {
133         delete &d;
134 }
135
136
137 void GuiView::close()
138 {
139         QMainWindow::close();
140 }
141
142 QMenu* GuiView::createPopupMenu()
143 {
144         // disable toolbar popup menu 
145         // Qt docs: Ownership of the popup menu is transferred to the caller.
146         return new QMenu;
147 }
148
149 void GuiView::init()
150 {
151         menubar_.reset(new QLMenubar(this, menubackend));
152         QObject::connect(menuBar(), SIGNAL(triggered(QAction *)),
153                 this, SLOT(updateMenu(QAction *)));
154
155         getToolbars().init();
156
157         statusBar()->setSizeGripEnabled(false);
158
159         QObject::connect(&statusbar_timer_, SIGNAL(timeout()),
160                 this, SLOT(update_view_state_qt()));
161
162         if (!work_area_->bufferView().buffer() && !theBufferList().empty())
163                 setBuffer(theBufferList().first());
164
165         // make sure the buttons are disabled if needed
166         updateToolbars();
167         updateLayoutChoice();
168         updateMenubar();
169 }
170
171
172 void GuiView::saveGeometry()
173 {
174         // FIXME:
175         // change the ifdef to 'geometry = normalGeometry();' only
176         // when Trolltech has fixed the broken normalGeometry on X11:
177         // http://www.trolltech.com/developer/task-tracker/index_html?id=119684+&method=entry
178         // Then also the moveEvent, resizeEvent, and the
179         // code for floatingGeometry_ can be removed;
180         // adjust GuiView::setGeometry()
181 #ifdef Q_WS_WIN
182         QRect geometry = normalGeometry();
183 #else
184         updateFloatingGeometry();
185         QRect geometry = floatingGeometry_;
186 #endif
187
188         // save windows size and position
189         Session & session = LyX::ref().session();
190         session.sessionInfo().save("WindowWidth", convert<string>(geometry.width()));
191         session.sessionInfo().save("WindowHeight", convert<string>(geometry.height()));
192         session.sessionInfo().save("WindowIsMaximized", (isMaximized() ? "yes" : "no"));
193         if (lyxrc.geometry_xysaved) {
194                 session.sessionInfo().save("WindowPosX", convert<string>(geometry.x() + d.posx_offset));
195                 session.sessionInfo().save("WindowPosY", convert<string>(geometry.y() + d.posy_offset));
196         }
197         getToolbars().saveToolbarInfo();
198 }
199                                                   
200 void GuiView::setGeometry(unsigned int width,
201                                                                   unsigned int height,
202                                                                   int posx, int posy,
203                                                                   bool maximize)
204 {
205         // only true when the -geometry option was NOT used
206         if (width != 0 && height != 0) {
207                 if (posx != -1 && posy != -1) {
208                         // if there are ever startup positioning problems 
209                         // on a virtual desktop then check the 6 lines below
210                         // http://doc.trolltech.com/4.2/qdesktopwidget.html 
211                         QDesktopWidget& dw = *qApp->desktop();
212                         QRect desk = dw.availableGeometry(dw.primaryScreen());
213                         (posx >= desk.width() ? posx = 50 : true);
214                         (posy >= desk.height()? posy = 50 : true);
215 #ifdef Q_WS_WIN
216                         // FIXME: use setGeometry only when Trolltech has fixed the qt4/X11 bug
217                         QWidget::setGeometry(posx, posy, width, height);
218 #else
219                         resize(width, height);
220                         move(posx, posy);
221 #endif
222                 } else {
223                         resize(width, height);
224                 }
225
226                 if (maximize)
227                         setWindowState(Qt::WindowMaximized);
228         }
229
230         show();
231
232         // For an unknown reason, the Window title update is not effective for
233         // the second windows up until it is shown on screen (Qt bug?).
234         updateWindowTitle();
235
236         // after show geometry() has changed (Qt bug?)
237         // we compensate the drift when storing the position
238         d.posx_offset = 0;
239         d.posy_offset = 0;
240         if (width != 0 && height != 0) 
241                 if (posx != -1 && posy != -1) {
242 #ifdef Q_WS_WIN
243                         d.posx_offset = posx - normalGeometry().x();
244                         d.posy_offset = posy - normalGeometry().y();
245 #else
246                         if (!maximize) {
247                                 d.posx_offset = posx - geometry().x();
248                                 d.posy_offset = posy - geometry().y();
249                         }
250 #endif
251                 }
252 }
253
254
255 void GuiView::updateMenu(QAction * /*action*/)
256 {
257         menubar_->update();
258 }
259
260
261 void GuiView::setWindowTitle(docstring const & t, docstring const & it)
262 {
263         QString title = windowTitle();
264         QString new_title = toqstr(t);
265         if (title != new_title) {
266                 QMainWindow::setWindowTitle(new_title);
267                 QMainWindow::setWindowIconText(toqstr(it));
268         }
269 }
270
271
272 void GuiView::addCommandBuffer(QToolBar * toolbar)
273 {
274         commandbuffer_ = new QCommandBuffer(this, *controlcommand_);
275         focus_command_buffer.connect(boost::bind(&GuiView::focus_command_widget, this));
276         toolbar->addWidget(commandbuffer_);
277 }
278
279
280 void GuiView::message(docstring const & str)
281 {
282         statusBar()->showMessage(toqstr(str));
283         statusbar_timer_.stop();
284         statusbar_timer_.start(statusbar_timer_value);
285 }
286
287
288 void GuiView::clearMessage()
289 {
290         update_view_state_qt();
291 }
292
293
294 void GuiView::focus_command_widget()
295 {
296         if (commandbuffer_)
297                 commandbuffer_->focus_command();
298 }
299
300
301 void GuiView::update_view_state_qt()
302 {
303         statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
304         statusbar_timer_.stop();
305 }
306
307 void GuiView::initTab(QWidget* workarea)
308 {
309         d.wt = new WidgetWithTabBar(workarea);
310         setCentralWidget(d.wt);
311         QObject::connect(d.wt->tabbar, SIGNAL(currentChanged(int)),
312                         this, SLOT(currentTabChanged(int)));
313 }
314
315 void GuiView::updateTab()
316 {
317         QTabBar& tb = *d.wt->tabbar;
318
319         // update when all  is done
320         tb.blockSignals(true);
321
322         typedef std::vector<string> Strings;
323         Strings const names = theBufferList().getFileNames();
324         size_t n_size = names.size();
325
326         Strings addtab;
327         // show tabs only when there is more 
328         // than one file opened
329         if (n_size > 1)
330         {
331                 for (size_t i = 0; i != n_size; i++) 
332                         if (d.namemap.find(names[i]) == d.namemap.end())
333                                 addtab.push_back(names.at(i));
334         }
335
336         for(size_t i = 0; i<addtab.size(); i++)
337         {
338                 QString tab_name = lyx::toqstr(onlyFilename(addtab.at(i))); 
339                 d.namemap.insert(GuiViewPrivate::NameMapPair(addtab.at(i), tab_name));
340                 tb.addTab(tab_name);
341         }
342
343         // check if all names showed by the tabs
344         // are also in the current bufferlist
345         Strings removetab;
346         bool notall = true;
347         if (n_size < 2)
348                 notall = false;
349         std::map<string, QString>::iterator tabit = d.namemap.begin();
350         for (;tabit != d.namemap.end(); ++tabit)
351         {
352                 bool found = false;
353                 for (size_t i = 0; i != n_size; i++) 
354                         if (tabit->first == names.at(i) && notall)
355                                 found = true;
356                 if (!found)
357                         removetab.push_back(tabit->first);
358         }
359         
360
361         // remove tabs
362         for(size_t i = 0; i<removetab.size(); i++)
363         {
364                 if (d.namemap.find(removetab.at(i)) != d.namemap.end())
365                 {
366                         tabit = d.namemap.find(removetab.at(i));
367                         for (int i = 0; i < tb.count(); i++)
368                                 if (tb.tabText(i) == tabit->second)
369                                 {
370                                         tb.removeTab(i);
371                                         break;
372                                 }
373                         d.namemap.erase(tabit);
374                 }
375         }
376
377         // rebuild func map
378         if (removetab.size() > 0 || addtab.size() > 0)
379         {
380                 d.funcmap.clear();
381                 tabit = d.namemap.begin();
382                 for (;tabit != d.namemap.end(); ++tabit)
383                 {
384                         QTabBar& tb = *d.wt->tabbar;
385                         for (int i = 0; i < tb.count(); i++)
386                         {
387                                 if (tb.tabText(i) == tabit->second)
388                                 {
389                                         FuncRequest func(LFUN_BUFFER_SWITCH, tabit->first);
390                                         d.funcmap.insert(GuiViewPrivate::FuncMapPair(i, func));
391                                         break;
392                                 }
393                         }
394                 }
395         }
396
397         // set current tab
398         if (view()->buffer()) 
399         {
400                 string cur_title = view()->buffer()->fileName();
401                 if (d.namemap.find(cur_title) != d.namemap.end())
402                 {
403                         QString tabname = d.namemap.find(cur_title)->second;
404                         for (int i = 0; i < tb.count(); i++)
405                                 if (tb.tabText(i) == tabname)
406                                 {
407                                         tb.setCurrentIndex(i);
408                                         break;
409                                 }
410                 }
411         }
412
413         tb.blockSignals(false);
414         d.wt->update();
415 }
416
417 void GuiView::currentTabChanged (int index)
418 {
419         std::map<int, FuncRequest>::const_iterator it = d.funcmap.find(index);
420         if (it != d.funcmap.end())
421                 activated(it->second);
422 }
423
424
425 void GuiView::updateStatusBar()
426 {
427         // let the user see the explicit message
428         if (statusbar_timer_.isActive())
429                 return;
430
431         statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
432 }
433
434
435 void GuiView::activated(FuncRequest const & func)
436 {
437         dispatch(func);
438 }
439
440
441 bool GuiView::hasFocus() const
442 {
443         return qApp->activeWindow() == this;
444 }
445
446
447 void  GuiView::updateFloatingGeometry()
448 {
449         if (!isMaximized())
450                 floatingGeometry_ = QRect(x(), y(), width(), height());
451 }
452
453
454 void GuiView::resizeEvent(QResizeEvent *)
455 {
456         updateFloatingGeometry();
457 }
458
459
460 void GuiView::moveEvent(QMoveEvent *)
461 {
462         updateFloatingGeometry();
463 }
464
465
466 void GuiView::closeEvent(QCloseEvent * close_event)
467 {
468         GuiImplementation & gui 
469                 = static_cast<GuiImplementation &>(theApp->gui());
470
471         vector<int> const & view_ids = gui.viewIds();
472
473         if (view_ids.size() == 1 && !theBufferList().quitWriteAll()) {
474                 close_event->ignore();
475                 return;
476         }
477
478         saveGeometry();
479         gui.unregisterView(this);
480 }
481
482
483 void GuiView::show()
484 {
485         QMainWindow::setWindowTitle(qt_("LyX"));
486         QMainWindow::show();
487         updateFloatingGeometry();
488 }
489
490
491 void GuiView::busy(bool yes)
492 {
493         static_cast<GuiWorkArea *>(work_area_)->setUpdatesEnabled(!yes);
494
495         if (yes) {
496                 work_area_->stopBlinkingCursor();
497                 QApplication::setOverrideCursor(Qt::WaitCursor);
498         }
499         else {
500                 work_area_->startBlinkingCursor();
501                 QApplication::restoreOverrideCursor();
502         }
503 }
504
505
506 Toolbars::ToolbarPtr GuiView::makeToolbar(ToolbarBackend::Toolbar const & tbb)
507 {
508         QLToolbar * Tb = new QLToolbar(tbb, *this);
509         //static QLToolbar * lastTb = 0;
510
511         if (tbb.flags & ToolbarBackend::TOP) {
512                         addToolBar(Qt::TopToolBarArea, Tb);
513                         addToolBarBreak(Qt::TopToolBarArea);
514         }
515         if (tbb.flags & ToolbarBackend::BOTTOM) {
516                 addToolBar(Qt::BottomToolBarArea, Tb);
517                 /*
518                 // Qt bug:
519                 // http://www.trolltech.com/developer/task-tracker/index_html?id=137015&method=entry
520                 // Doesn't work because the toolbar will evtl. be hidden.
521                 if (lastTb)
522                         insertToolBarBreak(lastTb);
523                 lastTb = Tb;
524                 */
525         }
526         if (tbb.flags & ToolbarBackend::LEFT) {
527                 addToolBar(Qt::LeftToolBarArea, Tb);
528         }
529         if (tbb.flags & ToolbarBackend::RIGHT) {
530                 addToolBar(Qt::RightToolBarArea, Tb);
531         }
532
533         return Toolbars::ToolbarPtr(Tb);
534 }
535
536 } // namespace frontend
537 } // namespace lyx
538
539 #include "GuiView_moc.cpp"