]> git.lyx.org Git - features.git/blob - src/frontends/qt4/GuiView.C
0bdc5bfa5ce4dd74a1ebf9e2acd86c23ec257fda
[features.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         GuiViewPrivate()
104         {}
105 };
106
107 GuiView::GuiView(int id)
108         : QMainWindow(), LyXView(id), commandbuffer_(0), d(*new GuiViewPrivate)
109 {
110         setAttribute(Qt::WA_DeleteOnClose, true);
111         setAttribute(Qt::WA_QuitOnClose, true);
112
113 //      setToolButtonStyle(Qt::ToolButtonIconOnly);
114 //      setIconSize(QSize(12,12));
115
116 //      bufferview_.reset(new BufferView(this, width, height));
117
118 #ifndef Q_WS_MACX
119         //  assign an icon to main form. We do not do it under Qt/Mac,
120         //  since the icon is provided in the application bundle.
121         string const iconname = libFileSearch("images", "lyx", "xpm");
122         if (!iconname.empty())
123                 setWindowIcon(QPixmap(toqstr(iconname)));
124 #endif
125 }
126
127
128 GuiView::~GuiView()
129 {
130         delete &d;
131 }
132
133
134 void GuiView::close()
135 {
136         QMainWindow::close();
137 }
138
139
140 void GuiView::init()
141 {
142         menubar_.reset(new QLMenubar(this, menubackend));
143         QObject::connect(menuBar(), SIGNAL(triggered(QAction *)),
144                 this, SLOT(updateMenu(QAction *)));
145
146         getToolbars().init();
147
148         statusBar()->setSizeGripEnabled(false);
149
150         QObject::connect(&statusbar_timer_, SIGNAL(timeout()),
151                 this, SLOT(update_view_state_qt()));
152
153         if (!work_area_->bufferView().buffer() && !theBufferList().empty())
154                 setBuffer(theBufferList().first());
155
156         // make sure the buttons are disabled if needed
157         updateToolbars();
158         updateLayoutChoice();
159         updateMenubar();
160 }
161
162
163 void GuiView::saveGeometry()
164 {
165         // FIXME:
166         // change the ifdef to 'geometry = normalGeometry();' only
167         // when Trolltech has fixed the broken normalGeometry on X11:
168         // http://www.trolltech.com/developer/task-tracker/index_html?id=119684+&method=entry
169         // Then also the moveEvent, resizeEvent, and the
170         // code for floatingGeometry_ can be removed;
171         // adjust GuiView::setGeometry()
172 #ifdef Q_OS_WIN32
173         QRect geometry = normalGeometry();
174 #else
175         updateFloatingGeometry();
176         QRect geometry = floatingGeometry_;
177 #endif
178
179         // save windows size and position
180         Session & session = LyX::ref().session();
181         session.sessionInfo().save("WindowWidth", convert<string>(geometry.width()));
182         session.sessionInfo().save("WindowHeight", convert<string>(geometry.height()));
183         session.sessionInfo().save("WindowIsMaximized", (isMaximized() ? "yes" : "no"));
184         if (lyxrc.geometry_xysaved) {
185                 session.sessionInfo().save("WindowPosX", convert<string>(geometry.x()));
186                 session.sessionInfo().save("WindowPosY", convert<string>(geometry.y()));
187         }
188 }
189                                                   
190 void GuiView::setGeometry(unsigned int width,
191                                                                   unsigned int height,
192                                                                   int posx, int posy,
193                                                                   bool maximize)
194 {
195         // only true when the -geometry option was NOT used
196         if (width != 0 && height != 0) {
197                 if (posx != -1 && posy != -1) {
198                         // if there are ever startup positioning problems 
199                         // on a virtual desktop then check the 6 lines below
200                         // http://doc.trolltech.com/4.2/qdesktopwidget.html 
201                         QDesktopWidget& dw = *qApp->desktop();
202                         QRect desk = dw.availableGeometry(dw.primaryScreen());
203                         (posx >= desk.width() ? posx = 50 : true);
204                         (posy >= desk.height()? posy = 50 : true);
205 #ifdef Q_WS_WIN
206                         // FIXME: use only setGeoemtry when Trolltech has
207                         // fixed the qt4/X11 bug
208                         QMainWindow::setGeometry(posx, posy,width, height);
209 #else
210                         resize(width, height);
211                         move(posx, posy);
212 #endif
213                 } else {
214                         resize(width, height);
215                 }
216
217                 if (maximize)
218                         setWindowState(Qt::WindowMaximized);
219         }
220         
221         show();
222 }
223
224
225 void GuiView::updateMenu(QAction * /*action*/)
226 {
227         menubar_->update();
228 }
229
230
231 void GuiView::setWindowTitle(docstring const & t, docstring const & it)
232 {
233         QMainWindow::setWindowTitle(toqstr(t));
234         QMainWindow::setWindowIconText(toqstr(it));
235 }
236
237
238 void GuiView::addCommandBuffer(QToolBar * toolbar)
239 {
240         commandbuffer_ = new QCommandBuffer(this, *controlcommand_);
241         focus_command_buffer.connect(boost::bind(&GuiView::focus_command_widget, this));
242         toolbar->addWidget(commandbuffer_);
243 }
244
245
246 void GuiView::message(docstring const & str)
247 {
248         statusBar()->showMessage(toqstr(str));
249         statusbar_timer_.stop();
250         statusbar_timer_.start(statusbar_timer_value);
251 }
252
253
254 void GuiView::clearMessage()
255 {
256         update_view_state_qt();
257 }
258
259
260 void GuiView::focus_command_widget()
261 {
262         if (commandbuffer_)
263                 commandbuffer_->focus_command();
264 }
265
266
267 void GuiView::update_view_state_qt()
268 {
269         statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
270         statusbar_timer_.stop();
271 }
272
273 void GuiView::initTab(QWidget* workarea)
274 {
275         d.wt = new WidgetWithTabBar(workarea);
276         setCentralWidget(d.wt);
277         QObject::connect(d.wt->tabbar, SIGNAL(currentChanged(int)),
278                         this, SLOT(currentTabChanged(int)));
279 }
280
281 void GuiView::updateTab()
282 {
283         QTabBar& tb = *d.wt->tabbar;
284
285         // update when all  is done
286         tb.blockSignals(true);
287
288         typedef std::vector<string> Strings;
289         Strings const names = theBufferList().getFileNames();
290         size_t n_size = names.size();
291
292         Strings addtab;
293         // show tabs only when there is more 
294         // than one file opened
295         if (n_size > 1)
296         {
297                 for (size_t i = 0; i != n_size; i++) 
298                         if (d.namemap.find(names[i]) == d.namemap.end())
299                                 addtab.push_back(names.at(i));
300         }
301
302         for(size_t i = 0; i<addtab.size(); i++)
303         {
304                 QString tab_name = lyx::toqstr(onlyFilename(addtab.at(i))); 
305                 d.namemap.insert(GuiViewPrivate::NameMapPair(addtab.at(i), tab_name));
306                 tb.addTab(tab_name);
307         }
308
309         // check if all names showed by the tabs
310         // are also in the current bufferlist
311         Strings removetab;
312         bool notall = true;
313         if (n_size < 2)
314                 notall = false;
315         std::map<string, QString>::iterator tabit = d.namemap.begin();
316         for (;tabit != d.namemap.end(); ++tabit)
317         {
318                 bool found = false;
319                 for (size_t i = 0; i != n_size; i++) 
320                         if (tabit->first == names.at(i) && notall)
321                                 found = true;
322                 if (!found)
323                         removetab.push_back(tabit->first);
324         }
325         
326
327         // remove tabs
328         for(size_t i = 0; i<removetab.size(); i++)
329         {
330                 if (d.namemap.find(removetab.at(i)) != d.namemap.end())
331                 {
332                         tabit = d.namemap.find(removetab.at(i));
333                         for (int i = 0; i < tb.count(); i++)
334                                 if (tb.tabText(i) == tabit->second)
335                                 {
336                                         tb.removeTab(i);
337                                         break;
338                                 }
339                         d.namemap.erase(tabit);
340                 }
341         }
342
343         // rebuild func map
344         if (removetab.size() > 0 || addtab.size() > 0)
345         {
346                 d.funcmap.clear();
347                 tabit = d.namemap.begin();
348                 for (;tabit != d.namemap.end(); ++tabit)
349                 {
350                         QTabBar& tb = *d.wt->tabbar;
351                         for (int i = 0; i < tb.count(); i++)
352                         {
353                                 if (tb.tabText(i) == tabit->second)
354                                 {
355                                         FuncRequest func(LFUN_BUFFER_SWITCH, tabit->first);
356                                         d.funcmap.insert(GuiViewPrivate::FuncMapPair(i, func));
357                                         break;
358                                 }
359                         }
360                 }
361         }
362
363         // set current tab
364         if (view()->buffer()) 
365         {
366                 string cur_title = view()->buffer()->fileName();
367                 if (d.namemap.find(cur_title) != d.namemap.end())
368                 {
369                         QString tabname = d.namemap.find(cur_title)->second;
370                         for (int i = 0; i < tb.count(); i++)
371                                 if (tb.tabText(i) == tabname)
372                                 {
373                                         tb.setCurrentIndex(i);
374                                         break;
375                                 }
376                 }
377         }
378
379         tb.blockSignals(false);
380         d.wt->update();
381 }
382
383 void GuiView::currentTabChanged (int index)
384 {
385         std::map<int, FuncRequest>::const_iterator it = d.funcmap.find(index);
386         if (it != d.funcmap.end())
387                 activated(it->second);
388 }
389
390
391 void GuiView::updateStatusBar()
392 {
393         // let the user see the explicit message
394         if (statusbar_timer_.isActive())
395                 return;
396
397         statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
398 }
399
400
401 void GuiView::activated(FuncRequest const & func)
402 {
403         dispatch(func);
404 }
405
406
407 bool GuiView::hasFocus() const
408 {
409         return qApp->activeWindow() == this;
410 }
411
412
413 void  GuiView::updateFloatingGeometry()
414 {
415         if (!isMaximized())
416                 floatingGeometry_ = QRect(x(), y(), width(), height());
417 }
418
419
420 void GuiView::resizeEvent(QResizeEvent *)
421 {
422         updateFloatingGeometry();
423 }
424
425
426 void GuiView::moveEvent(QMoveEvent *)
427 {
428         updateFloatingGeometry();
429 }
430
431
432 void GuiView::closeEvent(QCloseEvent * close_event)
433 {
434         GuiImplementation & gui 
435                 = static_cast<GuiImplementation &>(theApp->gui());
436
437         vector<int> const & view_ids = gui.viewIds();
438
439         if (view_ids.size() == 1 && !theBufferList().quitWriteAll()) {
440                 close_event->ignore();
441                 return;
442         }
443
444         saveGeometry();
445         gui.unregisterView(this);
446 }
447
448
449 void GuiView::show()
450 {
451         QMainWindow::setWindowTitle(qt_("LyX"));
452         QMainWindow::show();
453         updateFloatingGeometry();
454 }
455
456
457 void GuiView::busy(bool yes)
458 {
459         static_cast<GuiWorkArea *>(work_area_)->setUpdatesEnabled(!yes);
460
461         if (yes) {
462                 work_area_->stopBlinkingCursor();
463                 QApplication::setOverrideCursor(Qt::WaitCursor);
464         }
465         else {
466                 work_area_->startBlinkingCursor();
467                 QApplication::restoreOverrideCursor();
468         }
469 }
470
471
472 Toolbars::ToolbarPtr GuiView::makeToolbar(ToolbarBackend::Toolbar const & tbb)
473 {
474         QLToolbar * Tb = new QLToolbar(tbb, *this);
475         static QLToolbar * lastTb = 0;
476
477         if (tbb.flags & ToolbarBackend::TOP) {
478                         addToolBar(Qt::TopToolBarArea, Tb);
479                         addToolBarBreak(Qt::TopToolBarArea);
480         }
481         if (tbb.flags & ToolbarBackend::BOTTOM) {
482                 addToolBar(Qt::BottomToolBarArea, Tb);
483                 /*
484                 // Qt bug:
485                 // http://www.trolltech.com/developer/task-tracker/index_html?id=137015&method=entry
486                 // Doesn't work because the toolbar will evtl. be hidden.
487                 if (lastTb)
488                         insertToolBarBreak(lastTb);
489                 lastTb = Tb;
490                 */
491         }
492         if (tbb.flags & ToolbarBackend::LEFT) {
493                 addToolBar(Qt::LeftToolBarArea, Tb);
494         }
495         if (tbb.flags & ToolbarBackend::RIGHT) {
496                 addToolBar(Qt::RightToolBarArea, Tb);
497         }
498
499         return Toolbars::ToolbarPtr(Tb);
500 }
501
502 } // namespace frontend
503 } // namespace lyx
504
505 #include "GuiView_moc.cpp"