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