fb2main.cpp 32 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004
  1. #include <QtGui>
  2. #include <QtDebug>
  3. #include <QTreeView>
  4. #include <QWebFrame>
  5. #include "fb2main.hpp"
  6. #include "fb2code.hpp"
  7. #include "fb2dlgs.hpp"
  8. #include "fb2read.hpp"
  9. #include "fb2save.hpp"
  10. #include "fb2text.hpp"
  11. #include "fb2tree.hpp"
  12. #include "fb2head.hpp"
  13. #include "fb2utils.h"
  14. //---------------------------------------------------------------------------
  15. // FbDockWidget
  16. //---------------------------------------------------------------------------
  17. FbDockWidget::FbDockWidget(const QString &title, QWidget *parent, Qt::WindowFlags flags)
  18. : QDockWidget(title, parent, flags)
  19. {
  20. setFeatures(QDockWidget::AllDockWidgetFeatures);
  21. setAttribute(Qt::WA_DeleteOnClose);
  22. }
  23. //---------------------------------------------------------------------------
  24. // FbTextAction
  25. //---------------------------------------------------------------------------
  26. QAction * FbTextAction::action(QWebPage::WebAction action)
  27. {
  28. FbMainWindow * main = qobject_cast<FbMainWindow*>(parent());
  29. if (!main) return 0;
  30. FbTextPage * page = main->page();
  31. if (!page) return 0;
  32. return page->action(action);
  33. }
  34. //---------------------------------------------------------------------------
  35. // FbMainWindow
  36. //---------------------------------------------------------------------------
  37. FbMainWindow::FbMainWindow(const QString &filename, ViewMode mode)
  38. : QMainWindow()
  39. , textFrame(0)
  40. , codeEdit(0)
  41. , headTree(0)
  42. , noteEdit(0)
  43. , toolEdit(0)
  44. , dockTree(0)
  45. , dockImgs(0)
  46. , inspector(0)
  47. , messageEdit(0)
  48. , isSwitched(false)
  49. , isUntitled(true)
  50. {
  51. connect(qApp, SIGNAL(logMessage(QString)), SLOT(logMessage(QString)));
  52. setUnifiedTitleAndToolBarOnMac(true);
  53. setAttribute(Qt::WA_DeleteOnClose);
  54. setWindowIcon(QIcon(":icon.ico"));
  55. createActions();
  56. createStatusBar();
  57. readSettings();
  58. setCurrentFile(filename);
  59. if (mode == FB2) {
  60. viewText();
  61. textFrame->view()->load(filename.isEmpty() ? ":blank.fb2" : filename);
  62. } else {
  63. viewCode();
  64. if (!filename.isEmpty()) loadXML(filename);
  65. }
  66. }
  67. FbTextPage * FbMainWindow::page()
  68. {
  69. return textFrame ? textFrame->view()->page() : 0;
  70. }
  71. void FbMainWindow::logMessage(const QString &message)
  72. {
  73. if (!messageEdit) {
  74. messageEdit = new QTextEdit(this);
  75. connect(messageEdit, SIGNAL(destroyed()), SLOT(logDestroyed()));
  76. QDockWidget * dock = new FbLogDock(tr("Message log"), this);
  77. dock->setAttribute(Qt::WA_DeleteOnClose);
  78. dock->setFeatures(QDockWidget::AllDockWidgetFeatures);
  79. dock->setWidget(messageEdit);
  80. addDockWidget(Qt::BottomDockWidgetArea, dock);
  81. messageEdit->setMaximumHeight(80);
  82. }
  83. messageEdit->append(message);
  84. }
  85. void FbMainWindow::logShowed()
  86. {
  87. messageEdit->setMaximumHeight(QWIDGETSIZE_MAX);
  88. }
  89. void FbMainWindow::logDestroyed()
  90. {
  91. messageEdit = NULL;
  92. }
  93. void FbMainWindow::treeDestroyed()
  94. {
  95. actionContents->setChecked(false);
  96. dockTree = NULL;
  97. }
  98. void FbMainWindow::imgsDestroyed()
  99. {
  100. actionPictures->setChecked(false);
  101. dockImgs = NULL;
  102. }
  103. bool FbMainWindow::loadXML(const QString &filename)
  104. {
  105. if (!filename.isEmpty()) {
  106. QFile file(filename);
  107. if (file.open(QFile::ReadOnly | QFile::Text)) {
  108. codeEdit->clear();
  109. return codeEdit->read(&file);
  110. }
  111. }
  112. return false;
  113. }
  114. void FbMainWindow::closeEvent(QCloseEvent *event)
  115. {
  116. if (maybeSave()) {
  117. writeSettings();
  118. event->accept();
  119. } else {
  120. event->ignore();
  121. }
  122. }
  123. void FbMainWindow::fileNew()
  124. {
  125. FbMainWindow *other = new FbMainWindow;
  126. other->move(x() + 40, y() + 40);
  127. other->show();
  128. }
  129. void FbMainWindow::fileOpen()
  130. {
  131. QString filename = QFileDialog::getOpenFileName(this, tr("Open file"), QString(), "Fiction book files (*.fb2)");
  132. if (filename.isEmpty()) return;
  133. FbMainWindow * existing = findFbMainWindow(filename);
  134. if (existing) {
  135. existing->show();
  136. existing->raise();
  137. existing->activateWindow();
  138. return;
  139. }
  140. if (textFrame) {
  141. if (isUntitled && !isWindowModified()) {
  142. setCurrentFile(filename);
  143. textFrame->view()->load(filename);
  144. } else {
  145. FbMainWindow * other = new FbMainWindow(filename, FB2);
  146. other->move(x() + 40, y() + 40);
  147. other->show();
  148. }
  149. } else if (codeEdit) {
  150. if (isUntitled && !isWindowModified()) {
  151. setCurrentFile(filename);
  152. loadXML(filename);
  153. } else {
  154. FbMainWindow * other = new FbMainWindow(filename, XML);
  155. other->move(x() + 40, y() + 40);
  156. other->show();
  157. }
  158. }
  159. }
  160. bool FbMainWindow::fileSave()
  161. {
  162. if (isUntitled) {
  163. return fileSaveAs();
  164. } else {
  165. return saveFile(curFile);
  166. }
  167. }
  168. bool FbMainWindow::fileSaveAs()
  169. {
  170. FbSaveDialog dlg(this, tr("Save As..."));
  171. dlg.selectFile(curFile);
  172. if (!dlg.exec()) return false;
  173. QString fileName = dlg.fileName();
  174. if (fileName.isEmpty()) return false;
  175. return saveFile(fileName, dlg.codec());
  176. }
  177. void FbMainWindow::about()
  178. {
  179. QMessageBox::about(this, tr("About fb2edit"),
  180. tr("The <b>fb2edit</b> is application for editing FB2-files."));
  181. }
  182. void FbMainWindow::documentWasModified()
  183. {
  184. setModified(isSwitched || codeEdit->isModified());
  185. }
  186. void FbMainWindow::cleanChanged(bool clean)
  187. {
  188. setModified(isSwitched || !clean);
  189. }
  190. void FbMainWindow::setModified(bool modified)
  191. {
  192. QFileInfo info = windowFilePath();
  193. QString title = info.fileName();
  194. if (modified) title += QString("[*]");
  195. title += appTitle();
  196. setWindowTitle(title);
  197. setWindowModified(modified);
  198. }
  199. void FbMainWindow::createActions()
  200. {
  201. QAction * act;
  202. QMenu * menu;
  203. QToolBar * tool;
  204. QList<QAction*> actions;
  205. menu = menuBar()->addMenu(tr("&File"));
  206. tool = addToolBar(tr("File"));
  207. tool->setMovable(false);
  208. act = new QAction(FbIcon("document-new"), tr("&New"), this);
  209. act->setPriority(QAction::LowPriority);
  210. act->setShortcuts(QKeySequence::New);
  211. act->setStatusTip(tr("Create a new file"));
  212. connect(act, SIGNAL(triggered()), this, SLOT(fileNew()));
  213. menu->addAction(act);
  214. tool->addAction(act);
  215. act = new QAction(FbIcon("document-open"), tr("&Open..."), this);
  216. act->setShortcuts(QKeySequence::Open);
  217. act->setStatusTip(tr("Open an existing file"));
  218. connect(act, SIGNAL(triggered()), this, SLOT(fileOpen()));
  219. menu->addAction(act);
  220. tool->addAction(act);
  221. act = new QAction(FbIcon("document-save"), tr("&Save"), this);
  222. act->setShortcuts(QKeySequence::Save);
  223. act->setStatusTip(tr("Save the document to disk"));
  224. connect(act, SIGNAL(triggered()), this, SLOT(fileSave()));
  225. menu->addAction(act);
  226. tool->addAction(act);
  227. act = new QAction(FbIcon("document-save-as"), tr("Save &As..."), this);
  228. act->setShortcuts(QKeySequence::SaveAs);
  229. act->setStatusTip(tr("Save the document under a new name"));
  230. connect(act, SIGNAL(triggered()), this, SLOT(fileSaveAs()));
  231. menu->addAction(act);
  232. menu->addSeparator();
  233. act = new QAction(FbIcon("window-close"), tr("&Close"), this);
  234. act->setShortcuts(QKeySequence::Close);
  235. act->setStatusTip(tr("Close this window"));
  236. connect(act, SIGNAL(triggered()), this, SLOT(close()));
  237. menu->addAction(act);
  238. act = new QAction(FbIcon("application-exit"), tr("E&xit"), this);
  239. act->setShortcuts(QKeySequence::Quit);
  240. act->setStatusTip(tr("Exit the application"));
  241. connect(act, SIGNAL(triggered()), qApp, SLOT(closeAllWindows()));
  242. menu->addAction(act);
  243. menuEdit = menu = menuBar()->addMenu(tr("&Edit"));
  244. connect(QApplication::clipboard(), SIGNAL(dataChanged()), this, SLOT(clipboardDataChanged()));
  245. actionUndo = act = new QAction(FbIcon("edit-undo"), tr("&Undo"), this);
  246. act->setPriority(QAction::LowPriority);
  247. act->setShortcut(QKeySequence::Undo);
  248. act->setEnabled(false);
  249. menu->addAction(act);
  250. actionRedo = act = new QAction(FbIcon("edit-redo"), tr("&Redo"), this);
  251. act->setPriority(QAction::LowPriority);
  252. act->setShortcut(QKeySequence::Redo);
  253. act->setEnabled(false);
  254. menu->addAction(act);
  255. menu->addSeparator();
  256. actionCut = act = new QAction(FbIcon("edit-cut"), tr("Cu&t"), this);
  257. act->setShortcutContext(Qt::WidgetShortcut);
  258. act->setPriority(QAction::LowPriority);
  259. act->setShortcuts(QKeySequence::Cut);
  260. act->setStatusTip(tr("Cut the current selection's contents to the clipboard"));
  261. act->setEnabled(false);
  262. menu->addAction(act);
  263. actionCopy = act = new QAction(FbIcon("edit-copy"), tr("&Copy"), this);
  264. act->setShortcutContext(Qt::WidgetShortcut);
  265. act->setPriority(QAction::LowPriority);
  266. act->setShortcuts(QKeySequence::Copy);
  267. act->setStatusTip(tr("Copy the current selection's contents to the clipboard"));
  268. act->setEnabled(false);
  269. menu->addAction(act);
  270. actionPaste = act = new QAction(FbIcon("edit-paste"), tr("&Paste"), this);
  271. act->setShortcutContext(Qt::WidgetShortcut);
  272. act->setPriority(QAction::LowPriority);
  273. act->setShortcuts(QKeySequence::Paste);
  274. act->setStatusTip(tr("Paste the clipboard's contents into the current selection"));
  275. menu->addAction(act);
  276. actionPasteText = act = new QAction(tr("Paste (no style)"), this);
  277. menu->addAction(act);
  278. clipboardDataChanged();
  279. menu->addSeparator();
  280. actionFind = act = new QAction(FbIcon("edit-find"), tr("&Find..."), this);
  281. act->setShortcuts(QKeySequence::Find);
  282. menu->addAction(act);
  283. actionReplace = act = new QAction(FbIcon("edit-find-replace"), tr("&Replace..."), this);
  284. menu->addAction(act);
  285. menu->addSeparator();
  286. act = new QAction(FbIcon("preferences-desktop"), tr("&Settings"), this);
  287. act->setShortcuts(QKeySequence::Preferences);
  288. act->setStatusTip(tr("Application settings"));
  289. connect(act, SIGNAL(triggered()), SLOT(openSettings()));
  290. menu->addAction(act);
  291. menu = menuBar()->addMenu(tr("&Insert", "Main menu"));
  292. actionImage = act = new QAction(FbIcon("insert-image"), tr("&Image"), this);
  293. menu->addAction(act);
  294. actionNote = act = new QAction(FbIcon("insert-text"), tr("&Footnote"), this);
  295. menu->addAction(act);
  296. actionLink = act = new QAction(FbIcon("insert-link"), tr("&Hiperlink"), this);
  297. menu->addAction(act);
  298. menu->addSeparator();
  299. actionBody = act = new QAction(tr("&Body"), this);
  300. menu->addAction(act);
  301. actionSection = act = new QAction(FbIcon("insert-object"), tr("&Section"), this);
  302. menu->addAction(act);
  303. actionTitle = act = new QAction(tr("&Title"), this);
  304. menu->addAction(act);
  305. actionEpigraph = act = new QAction(tr("&Epigraph"), this);
  306. menu->addAction(act);
  307. actionAnnot = act = new QAction(tr("&Annotation"), this);
  308. menu->addAction(act);
  309. actionSubtitle = act = new QAction(tr("&Subtitle"), this);
  310. menu->addAction(act);
  311. actionAuthor = act = new QAction(tr("&Cite"), this);
  312. menu->addAction(act);
  313. actionPoem = act = new QAction(tr("&Poem"), this);
  314. menu->addAction(act);
  315. actionStanza = act = new QAction(tr("&Stanza"), this);
  316. menu->addAction(act);
  317. actionAuthor = act = new QAction(tr("&Author"), this);
  318. menu->addAction(act);
  319. actionDate = act = new QAction(tr("&Date"), this);
  320. menu->addAction(act);
  321. menu->addSeparator();
  322. actionSimpleText = act = new QAction(tr("Simple text"), this);
  323. menu->addAction(act);
  324. actionParaSeparator = act = new QAction(tr("Paragraph"), this);
  325. menu->addAction(act);
  326. actionLineSeparator = act = new QAction(tr("Line end"), this);
  327. menu->addAction(act);
  328. menuText = menu = menuBar()->addMenu(tr("Fo&rmat"));
  329. actionClearFormat = act = new FbTextAction(FbIcon("edit-clear"), tr("Clear format"), QWebPage::RemoveFormat, this);
  330. menu->addAction(act);
  331. menu->addSeparator();
  332. actionTextBold = act = new FbTextAction(FbIcon("format-text-bold"), tr("&Bold"), QWebPage::ToggleBold, this);
  333. act->setShortcuts(QKeySequence::Bold);
  334. act->setCheckable(true);
  335. menu->addAction(act);
  336. actionTextItalic = act = new FbTextAction(FbIcon("format-text-italic"), tr("&Italic"), QWebPage::ToggleItalic, this);
  337. act->setShortcuts(QKeySequence::Italic);
  338. act->setCheckable(true);
  339. menu->addAction(act);
  340. actionTextStrike = act = new FbTextAction(FbIcon("format-text-strikethrough"), tr("&Strikethrough"), QWebPage::ToggleStrikethrough, this);
  341. act->setCheckable(true);
  342. menu->addAction(act);
  343. actionTextSup = act = new FbTextAction(FbIcon("format-text-superscript"), tr("Su&perscript"), QWebPage::ToggleSuperscript, this);
  344. act->setCheckable(true);
  345. menu->addAction(act);
  346. actionTextSub = act = new FbTextAction(FbIcon("format-text-subscript"), tr("Su&bscript"), QWebPage::ToggleSubscript, this);
  347. act->setCheckable(true);
  348. menu->addAction(act);
  349. actionTextCode = act = new QAction(FbIcon("utilities-terminal"), tr("&Code"), this);
  350. act->setCheckable(true);
  351. menu->addAction(act);
  352. menu->addSeparator();
  353. actionSectionAdd = act = new FbTextAction(FbIcon("format-indent-more"), tr("Create section"), QWebPage::ToggleSubscript, this);
  354. menu->addAction(act);
  355. actionSectionDel = act = new FbTextAction(FbIcon("format-indent-less"), tr("Remove section"), QWebPage::ToggleSubscript, this);
  356. menu->addAction(act);
  357. actionTextTitle = act = new FbTextAction(FbIcon("format-justify-center"), tr("Make title"), QWebPage::ToggleSubscript, this);
  358. menu->addAction(act);
  359. menuView = menu = menuBar()->addMenu(tr("&View"));
  360. tool->addSeparator();
  361. QActionGroup * viewGroup = new QActionGroup(this);
  362. act = new QAction(tr("&Text"), this);
  363. act->setCheckable(true);
  364. act->setChecked(true);
  365. connect(act, SIGNAL(triggered()), this, SLOT(viewText()));
  366. viewGroup->addAction(act);
  367. menu->addAction(act);
  368. tool->addAction(act);
  369. act = new QAction(tr("&Head"), this);
  370. act->setCheckable(true);
  371. connect(act, SIGNAL(triggered()), this, SLOT(viewHead()));
  372. viewGroup->addAction(act);
  373. menu->addAction(act);
  374. tool->addAction(act);
  375. act = new QAction(tr("&XML"), this);
  376. act->setCheckable(true);
  377. connect(act, SIGNAL(triggered()), this, SLOT(viewCode()));
  378. viewGroup->addAction(act);
  379. menu->addAction(act);
  380. tool->addAction(act);
  381. #ifdef QT_DEBUG
  382. act = new QAction(tr("&HTML"), this);
  383. act->setCheckable(true);
  384. connect(act, SIGNAL(triggered()), this, SLOT(viewHtml()));
  385. viewGroup->addAction(act);
  386. menu->addAction(act);
  387. #endif // _DEBUG
  388. menu->addSeparator();
  389. actionZoomIn = act = new QAction(FbIcon("zoom-in"), tr("Zoom in"), this);
  390. act->setShortcuts(QKeySequence::ZoomIn);
  391. menu->addAction(act);
  392. actionZoomOut = act = new QAction(FbIcon("zoom-out"), tr("Zoom out"), this);
  393. act->setShortcuts(QKeySequence::ZoomOut);
  394. menu->addAction(act);
  395. actionZoomReset = act = new QAction(FbIcon("zoom-original"), tr("Zoom original"), this);
  396. menu->addAction(act);
  397. menu->addSeparator();
  398. actionContents = act = new QAction(tr("&Contents"), this);
  399. act->setCheckable(true);
  400. connect(act, SIGNAL(triggered()), this, SLOT(viewTree()));
  401. menu->addAction(act);
  402. actionPictures = act = new QAction(tr("&Pictures"), this);
  403. act->setCheckable(true);
  404. connect(act, SIGNAL(triggered()), this, SLOT(viewImgs()));
  405. menu->addAction(act);
  406. actionInspect = act = new QAction(tr("&Web inspector"), this);
  407. act->setCheckable(true);
  408. connect(this, SIGNAL(showInspectorChecked(bool)), actionInspect, SLOT(setChecked(bool)));
  409. menu->addAction(act);
  410. menuBar()->addSeparator();
  411. menu = menuBar()->addMenu(tr("&Help"));
  412. act = new QAction(FbIcon("help-about"), tr("&About"), this);
  413. act->setStatusTip(tr("Show the application's About box"));
  414. connect(act, SIGNAL(triggered()), this, SLOT(about()));
  415. menu->addAction(act);
  416. act = new QAction(tr("About &Qt"), this);
  417. act->setStatusTip(tr("Show the Qt library's About box"));
  418. connect(act, SIGNAL(triggered()), qApp, SLOT(aboutQt()));
  419. menu->addAction(act);
  420. }
  421. void FbMainWindow::openSettings()
  422. {
  423. FbSetupDlg dlg(this);
  424. dlg.exec();
  425. }
  426. void FbMainWindow::createTree()
  427. {
  428. if (textFrame && centralWidget() == textFrame) {
  429. dockTree = new FbDockWidget(tr("Contents"), this);
  430. dockTree->setWidget(new FbTreeWidget(textFrame->view(), this));
  431. connect(dockTree, SIGNAL(visibilityChanged(bool)), actionContents, SLOT(setChecked(bool)));
  432. connect(dockTree, SIGNAL(destroyed()), SLOT(treeDestroyed()));
  433. addDockWidget(Qt::LeftDockWidgetArea, dockTree);
  434. }
  435. }
  436. void FbMainWindow::createImgs()
  437. {
  438. if (textFrame && centralWidget() == textFrame) {
  439. dockImgs = new FbDockWidget(tr("Pictures"), this);
  440. dockImgs->setWidget(new FbListWidget(textFrame->view(), this));
  441. connect(dockImgs, SIGNAL(visibilityChanged(bool)), actionPictures, SLOT(setChecked(bool)));
  442. connect(dockImgs, SIGNAL(destroyed()), SLOT(imgsDestroyed()));
  443. addDockWidget(Qt::RightDockWidgetArea, dockImgs);
  444. }
  445. }
  446. void FbMainWindow::selectionChanged()
  447. {
  448. FbTextEdit *view = textFrame->view();
  449. actionCut->setEnabled(view->actionEnabled(QWebPage::Cut));
  450. actionCopy->setEnabled(view->actionEnabled(QWebPage::Copy));
  451. statusBar()->showMessage(view->page()->status());
  452. }
  453. void FbMainWindow::canUndoChanged(bool canUndo)
  454. {
  455. actionUndo->setEnabled(canUndo);
  456. }
  457. void FbMainWindow::canRedoChanged(bool canRedo)
  458. {
  459. actionRedo->setEnabled(canRedo);
  460. }
  461. void FbMainWindow::undoChanged()
  462. {
  463. actionUndo->setEnabled(textFrame->view()->actionEnabled(QWebPage::Undo));
  464. }
  465. void FbMainWindow::redoChanged()
  466. {
  467. actionRedo->setEnabled(textFrame->view()->actionEnabled(QWebPage::Redo));
  468. }
  469. void FbMainWindow::createStatusBar()
  470. {
  471. statusBar()->showMessage(tr("Ready"));
  472. }
  473. void FbMainWindow::readSettings()
  474. {
  475. QSettings settings;
  476. QPoint pos = settings.value("pos", QPoint(200, 200)).toPoint();
  477. QSize size = settings.value("size", QSize(400, 400)).toSize();
  478. move(pos);
  479. resize(size);
  480. }
  481. void FbMainWindow::writeSettings()
  482. {
  483. QSettings settings;
  484. settings.setValue("pos", pos());
  485. settings.setValue("size", size());
  486. }
  487. bool FbMainWindow::maybeSave()
  488. {
  489. if (textFrame && textFrame->view()->isModified()) {
  490. QMessageBox::StandardButton ret;
  491. ret = QMessageBox::warning(this, qApp->applicationName(),
  492. tr("The document has been modified. Do you want to save your changes?"),
  493. QMessageBox::Save | QMessageBox::Discard
  494. | QMessageBox::Cancel);
  495. if (ret == QMessageBox::Save)
  496. return fileSave();
  497. else if (ret == QMessageBox::Cancel)
  498. return false;
  499. }
  500. return true;
  501. }
  502. bool FbMainWindow::saveFile(const QString &fileName, const QString &codec)
  503. {
  504. QFile file(fileName);
  505. if (!file.open(QFile::WriteOnly | QFile::Text)) {
  506. QMessageBox::warning(this, qApp->applicationName(), tr("Cannot write file %1: %2.").arg(fileName).arg(file.errorString()));
  507. return false;
  508. }
  509. if (textFrame) {
  510. isSwitched = false;
  511. textFrame->view()->save(&file, codec);
  512. setCurrentFile(fileName);
  513. return true;
  514. }
  515. if (codeEdit) {
  516. QTextStream out(&file);
  517. out << codeEdit->toPlainText();
  518. setCurrentFile(fileName);
  519. return true;
  520. }
  521. return false;
  522. }
  523. void FbMainWindow::setCurrentFile(const QString &filename)
  524. {
  525. if (filename.isEmpty()) {
  526. static int sequenceNumber = 1;
  527. curFile = QString("book%1.fb2").arg(sequenceNumber++);
  528. } else {
  529. QFileInfo info = filename;
  530. curFile = info.canonicalFilePath();
  531. }
  532. setWindowFilePath(curFile);
  533. setModified(false);
  534. }
  535. QString FbMainWindow::appTitle() const
  536. {
  537. return QString(" - ") += qApp->applicationName() += QString(" ") += qApp->applicationVersion();
  538. }
  539. FbMainWindow *FbMainWindow::findFbMainWindow(const QString &fileName)
  540. {
  541. QString canonicalFilePath = QFileInfo(fileName).canonicalFilePath();
  542. foreach (QWidget *widget, qApp->topLevelWidgets()) {
  543. FbMainWindow *mainWin = qobject_cast<FbMainWindow *>(widget);
  544. if (mainWin && mainWin->curFile == canonicalFilePath)
  545. return mainWin;
  546. }
  547. return 0;
  548. }
  549. void FbMainWindow::checkScintillaUndo()
  550. {
  551. if (!codeEdit) return;
  552. actionUndo->setEnabled(codeEdit->isUndoAvailable());
  553. actionRedo->setEnabled(codeEdit->isRedoAvailable());
  554. }
  555. void FbMainWindow::createTextToolbar()
  556. {
  557. FbTextEdit * textEdit = textFrame->view();
  558. FbTextPage * textPage = textEdit->page();
  559. connect(textPage->undoStack(), SIGNAL(cleanChanged(bool)), SLOT(cleanChanged(bool)));
  560. connect(textPage->undoStack(), SIGNAL(canUndoChanged(bool)), SLOT(canUndoChanged(bool)));
  561. connect(textPage->undoStack(), SIGNAL(canRedoChanged(bool)), SLOT(canRedoChanged(bool)));
  562. connect(textPage, SIGNAL(selectionChanged()), SLOT(selectionChanged()));
  563. connect(actionUndo, SIGNAL(triggered()), textEdit->pageAction(QWebPage::Undo), SIGNAL(triggered()));
  564. connect(actionRedo, SIGNAL(triggered()), textEdit->pageAction(QWebPage::Redo), SIGNAL(triggered()));
  565. connect(actionCut, SIGNAL(triggered()), textEdit->pageAction(QWebPage::Cut), SIGNAL(triggered()));
  566. connect(actionCopy, SIGNAL(triggered()), textEdit->pageAction(QWebPage::Copy), SIGNAL(triggered()));
  567. connect(actionPaste, SIGNAL(triggered()), textEdit->pageAction(QWebPage::Paste), SIGNAL(triggered()));
  568. connect(actionPasteText, SIGNAL(triggered()), textEdit->pageAction(QWebPage::PasteAndMatchStyle), SIGNAL(triggered()));
  569. connect(actionClearFormat, SIGNAL(triggered()), textEdit->pageAction(QWebPage::RemoveFormat), SIGNAL(triggered()));
  570. connect(actionTextBold, SIGNAL(triggered()), textEdit->pageAction(QWebPage::ToggleBold), SIGNAL(triggered()));
  571. connect(actionTextItalic, SIGNAL(triggered()), textEdit->pageAction(QWebPage::ToggleItalic), SIGNAL(triggered()));
  572. connect(actionTextStrike, SIGNAL(triggered()), textEdit->pageAction(QWebPage::ToggleStrikethrough), SIGNAL(triggered()));
  573. connect(actionTextSub, SIGNAL(triggered()), textEdit->pageAction(QWebPage::ToggleSubscript), SIGNAL(triggered()));
  574. connect(actionTextSup, SIGNAL(triggered()), textEdit->pageAction(QWebPage::ToggleSuperscript), SIGNAL(triggered()));
  575. connect(textEdit->pageAction(QWebPage::RemoveFormat), SIGNAL(changed()), actionClearFormat, SLOT(updateEnabled()));
  576. connect(textEdit->pageAction(QWebPage::ToggleBold), SIGNAL(changed()), actionTextBold, SLOT(updateChecked()));
  577. connect(textEdit->pageAction(QWebPage::ToggleItalic), SIGNAL(changed()), actionTextItalic, SLOT(updateChecked()));
  578. connect(textEdit->pageAction(QWebPage::ToggleStrikethrough), SIGNAL(changed()), actionTextStrike, SLOT(updateChecked()));
  579. connect(textEdit->pageAction(QWebPage::ToggleSubscript), SIGNAL(changed()), actionTextSub, SLOT(updateChecked()));
  580. connect(textEdit->pageAction(QWebPage::ToggleSuperscript), SIGNAL(changed()), actionTextSup, SLOT(updateChecked()));
  581. connect(actionFind, SIGNAL(triggered()), textEdit, SLOT(find()));
  582. connect(actionImage, SIGNAL(triggered()), textEdit, SLOT(insertImage()));
  583. connect(actionNote, SIGNAL(triggered()), textEdit, SLOT(insertNote()));
  584. connect(actionLink, SIGNAL(triggered()), textEdit, SLOT(insertLink()));
  585. connect(actionTitle, SIGNAL(triggered()), textPage, SLOT(insertTitle()));
  586. connect(actionAnnot, SIGNAL(triggered()), textPage, SLOT(insertAnnot()));
  587. connect(actionAuthor, SIGNAL(triggered()), textPage, SLOT(insertAuthor()));
  588. connect(actionEpigraph, SIGNAL(triggered()), textPage, SLOT(insertEpigraph()));
  589. connect(actionSubtitle, SIGNAL(triggered()), textPage, SLOT(insertSubtitle()));
  590. connect(actionSection, SIGNAL(triggered()), textPage, SLOT(insertSection()));
  591. connect(actionStanza, SIGNAL(triggered()), textPage, SLOT(insertStanza()));
  592. connect(actionPoem, SIGNAL(triggered()), textPage, SLOT(insertPoem()));
  593. connect(actionDate, SIGNAL(triggered()), textPage, SLOT(insertDate()));
  594. connect(actionBody, SIGNAL(triggered()), textPage, SLOT(insertBody()));
  595. connect(actionSimpleText, SIGNAL(triggered()), textPage, SLOT(insertText()));
  596. connect(actionParaSeparator, SIGNAL(triggered()), textEdit->pageAction(QWebPage::InsertParagraphSeparator), SIGNAL(triggered()));
  597. connect(actionLineSeparator, SIGNAL(triggered()), textEdit->pageAction(QWebPage::InsertLineSeparator), SIGNAL(triggered()));
  598. connect(actionSectionAdd, SIGNAL(triggered()), textPage, SLOT(createSection()));
  599. connect(actionSectionDel, SIGNAL(triggered()), textPage, SLOT(deleteSection()));
  600. connect(actionTextTitle, SIGNAL(triggered()), textPage, SLOT(createTitle()));
  601. connect(actionZoomIn, SIGNAL(triggered()), textEdit, SLOT(zoomIn()));
  602. connect(actionZoomOut, SIGNAL(triggered()), textEdit, SLOT(zoomOut()));
  603. connect(actionZoomReset, SIGNAL(triggered()), textEdit, SLOT(zoomReset()));
  604. FB2DELETE(toolEdit);
  605. QToolBar *tool = toolEdit = addToolBar(tr("Edit"));
  606. tool->setMovable(false);
  607. tool->addSeparator();
  608. textEdit->addTools(tool);
  609. tool->addSeparator();
  610. tool->addAction(actionSectionAdd);
  611. tool->addAction(actionSectionDel);
  612. tool->addAction(actionTextTitle);
  613. tool->addSeparator();
  614. tool->addAction(actionImage);
  615. tool->addAction(actionNote);
  616. tool->addAction(actionLink);
  617. tool->addAction(actionSection);
  618. }
  619. void FbMainWindow::viewText()
  620. {
  621. if (textFrame && centralWidget() == textFrame) return;
  622. if (textFrame) textFrame->hideInspector();
  623. bool load = false;
  624. QString xml;
  625. if (codeEdit) {
  626. xml = codeEdit->text();
  627. isSwitched = true;
  628. load = true;
  629. }
  630. FB2DELETE(codeEdit);
  631. FB2DELETE(headTree);
  632. if (textFrame) {
  633. createTextToolbar();
  634. } else {
  635. textFrame = new FbTextFrame(this, actionInspect);
  636. }
  637. setCentralWidget(textFrame);
  638. viewTree();
  639. FbTextEdit *textEdit = textFrame->view();
  640. connect(textEdit, SIGNAL(loadFinished(bool)), SLOT(createTextToolbar()));
  641. connect(textEdit->pageAction(QWebPage::Undo), SIGNAL(changed()), SLOT(undoChanged()));
  642. connect(textEdit->pageAction(QWebPage::Redo), SIGNAL(changed()), SLOT(redoChanged()));
  643. if (load) textFrame->view()->load(curFile, xml);
  644. actionContents->setEnabled(true);
  645. actionPictures->setEnabled(true);
  646. actionInspect->setEnabled(true);
  647. textFrame->view()->setFocus();
  648. }
  649. void FbMainWindow::viewHead()
  650. {
  651. if (headTree && centralWidget() == headTree) return;
  652. if (textFrame) textFrame->hideInspector();
  653. QString xml;
  654. if (codeEdit) xml = codeEdit->text();
  655. FB2DELETE(dockTree);
  656. FB2DELETE(dockImgs);
  657. FB2DELETE(codeEdit);
  658. FB2DELETE(toolEdit);
  659. if (!textFrame) {
  660. textFrame = new FbTextFrame(this, actionInspect);
  661. }
  662. if (!headTree) {
  663. headTree = new FbHeadView(textFrame->view(), this);
  664. connect(headTree, SIGNAL(status(QString)), this, SLOT(status(QString)));
  665. }
  666. this->setFocus();
  667. textFrame->setParent(NULL);
  668. setCentralWidget(headTree);
  669. textFrame->setParent(this);
  670. headTree->updateTree();
  671. headTree->setFocus();
  672. if (!xml.isEmpty()) textFrame->view()->load(curFile, xml);
  673. if (textFrame) {
  674. actionUndo->disconnect();
  675. actionRedo->disconnect();
  676. actionCut->disconnect();
  677. actionCopy->disconnect();
  678. actionPaste->disconnect();
  679. actionTextBold->disconnect();
  680. actionTextItalic->disconnect();
  681. actionTextStrike->disconnect();
  682. actionTextSub->disconnect();
  683. actionTextSup->disconnect();
  684. }
  685. FB2DELETE(toolEdit);
  686. toolEdit = addToolBar(tr("Edit"));
  687. headTree->initToolbar(*toolEdit);
  688. toolEdit->addSeparator();
  689. toolEdit->setMovable(false);
  690. actionContents->setEnabled(false);
  691. actionPictures->setEnabled(false);
  692. actionInspect->setEnabled(true);
  693. }
  694. void FbMainWindow::viewCode()
  695. {
  696. if (codeEdit && centralWidget() == codeEdit) return;
  697. bool load = false;
  698. QByteArray xml;
  699. if (textFrame) {
  700. textFrame->view()->save(&xml);
  701. isSwitched = true;
  702. load = true;
  703. }
  704. FB2DELETE(textFrame);
  705. FB2DELETE(dockTree);
  706. FB2DELETE(dockImgs);
  707. FB2DELETE(headTree);
  708. if (!codeEdit) {
  709. codeEdit = new FbCodeEdit;
  710. }
  711. if (load) codeEdit->load(xml);
  712. setCentralWidget(codeEdit);
  713. codeEdit->setFocus();
  714. FB2DELETE(toolEdit);
  715. QToolBar *tool = toolEdit = addToolBar(tr("Edit"));
  716. tool->addSeparator();
  717. tool->addAction(actionUndo);
  718. tool->addAction(actionRedo);
  719. tool->addSeparator();
  720. tool->addAction(actionCut);
  721. tool->addAction(actionCopy);
  722. tool->addAction(actionPaste);
  723. tool->addSeparator();
  724. tool->addAction(actionZoomIn);
  725. tool->addAction(actionZoomOut);
  726. tool->addAction(actionZoomReset);
  727. tool->setMovable(false);
  728. connect(codeEdit, SIGNAL(textChanged()), this, SLOT(documentWasModified()));
  729. connect(codeEdit, SIGNAL(textChanged()), this, SLOT(checkScintillaUndo()));
  730. connect(codeEdit, SIGNAL(copyAvailable(bool)), actionCut, SLOT(setEnabled(bool)));
  731. connect(codeEdit, SIGNAL(copyAvailable(bool)), actionCopy, SLOT(setEnabled(bool)));
  732. connect(actionUndo, SIGNAL(triggered()), codeEdit, SLOT(undo()));
  733. connect(actionRedo, SIGNAL(triggered()), codeEdit, SLOT(redo()));
  734. connect(actionCut, SIGNAL(triggered()), codeEdit, SLOT(cut()));
  735. connect(actionCopy, SIGNAL(triggered()), codeEdit, SLOT(copy()));
  736. connect(actionPaste, SIGNAL(triggered()), codeEdit, SLOT(paste()));
  737. connect(actionFind, SIGNAL(triggered()), codeEdit, SLOT(find()));
  738. connect(actionZoomIn, SIGNAL(triggered()), codeEdit, SLOT(zoomIn()));
  739. connect(actionZoomOut, SIGNAL(triggered()), codeEdit, SLOT(zoomOut()));
  740. connect(actionZoomReset, SIGNAL(triggered()), codeEdit, SLOT(zoomReset()));
  741. actionContents->setEnabled(false);
  742. actionPictures->setEnabled(false);
  743. actionInspect->setEnabled(false);
  744. }
  745. void FbMainWindow::viewHtml()
  746. {
  747. if (codeEdit && centralWidget() == codeEdit) return;
  748. if (!textFrame) return;
  749. QString html = textFrame->view()->page()->mainFrame()->toHtml();
  750. isSwitched = true;
  751. FB2DELETE(textFrame);
  752. FB2DELETE(dockTree);
  753. FB2DELETE(dockImgs);
  754. FB2DELETE(headTree);
  755. if (!codeEdit) {
  756. codeEdit = new FbCodeEdit;
  757. }
  758. codeEdit->setPlainText(html);
  759. setCentralWidget(codeEdit);
  760. codeEdit->setFocus();
  761. FB2DELETE(toolEdit);
  762. QToolBar *tool = toolEdit = addToolBar(tr("Edit"));
  763. tool->addSeparator();
  764. tool->addAction(actionUndo);
  765. tool->addAction(actionRedo);
  766. tool->addSeparator();
  767. tool->addAction(actionCut);
  768. tool->addAction(actionCopy);
  769. tool->addAction(actionPaste);
  770. tool->addSeparator();
  771. tool->addAction(actionZoomIn);
  772. tool->addAction(actionZoomOut);
  773. tool->addAction(actionZoomReset);
  774. tool->setMovable(false);
  775. connect(codeEdit, SIGNAL(textChanged()), this, SLOT(documentWasModified()));
  776. connect(codeEdit, SIGNAL(textChanged()), this, SLOT(checkScintillaUndo()));
  777. connect(codeEdit, SIGNAL(copyAvailable(bool)), actionCut, SLOT(setEnabled(bool)));
  778. connect(codeEdit, SIGNAL(copyAvailable(bool)), actionCopy, SLOT(setEnabled(bool)));
  779. connect(actionUndo, SIGNAL(triggered()), codeEdit, SLOT(undo()));
  780. connect(actionRedo, SIGNAL(triggered()), codeEdit, SLOT(redo()));
  781. connect(actionCut, SIGNAL(triggered()), codeEdit, SLOT(cut()));
  782. connect(actionCopy, SIGNAL(triggered()), codeEdit, SLOT(copy()));
  783. connect(actionPaste, SIGNAL(triggered()), codeEdit, SLOT(paste()));
  784. connect(actionFind, SIGNAL(triggered()), codeEdit, SLOT(find()));
  785. connect(actionZoomIn, SIGNAL(triggered()), codeEdit, SLOT(zoomIn()));
  786. connect(actionZoomOut, SIGNAL(triggered()), codeEdit, SLOT(zoomOut()));
  787. connect(actionZoomReset, SIGNAL(triggered()), codeEdit, SLOT(zoomReset()));
  788. actionContents->setEnabled(false);
  789. actionPictures->setEnabled(false);
  790. actionInspect->setEnabled(false);
  791. }
  792. void FbMainWindow::viewTree()
  793. {
  794. if (dockTree) dockTree->deleteLater(); else createTree();
  795. }
  796. void FbMainWindow::viewImgs()
  797. {
  798. if (dockImgs) dockImgs->deleteLater(); else createImgs();
  799. }
  800. void FbMainWindow::clipboardDataChanged()
  801. {
  802. if (const QMimeData *md = QApplication::clipboard()->mimeData()) {
  803. actionPaste->setEnabled(md->hasText());
  804. actionPasteText->setEnabled(md->hasText());
  805. }
  806. }
  807. void FbMainWindow::status(const QString &text)
  808. {
  809. statusBar()->showMessage(text);
  810. }