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