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 QDockWidget(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. act = new QAction(tr("&HTML"), this);
  382. act->setCheckable(true);
  383. connect(act, SIGNAL(triggered()), this, SLOT(viewHtml()));
  384. viewGroup->addAction(act);
  385. menu->addAction(act);
  386. menu->addSeparator();
  387. actionZoomIn = act = new QAction(FbIcon("zoom-in"), tr("Zoom in"), this);
  388. act->setShortcuts(QKeySequence::ZoomIn);
  389. menu->addAction(act);
  390. actionZoomOut = act = new QAction(FbIcon("zoom-out"), tr("Zoom out"), this);
  391. act->setShortcuts(QKeySequence::ZoomOut);
  392. menu->addAction(act);
  393. actionZoomReset = act = new QAction(FbIcon("zoom-original"), tr("Zoom original"), this);
  394. menu->addAction(act);
  395. menu->addSeparator();
  396. actionContents = act = new QAction(tr("&Contents"), this);
  397. act->setCheckable(true);
  398. connect(act, SIGNAL(triggered()), this, SLOT(viewTree()));
  399. menu->addAction(act);
  400. actionPictures = act = new QAction(tr("&Pictures"), this);
  401. act->setCheckable(true);
  402. connect(act, SIGNAL(triggered()), this, SLOT(viewImgs()));
  403. menu->addAction(act);
  404. actionInspect = act = new QAction(tr("&Web inspector"), this);
  405. act->setCheckable(true);
  406. connect(this, SIGNAL(showInspectorChecked(bool)), actionInspect, SLOT(setChecked(bool)));
  407. menu->addAction(act);
  408. menuBar()->addSeparator();
  409. menu = menuBar()->addMenu(tr("&Help"));
  410. act = new QAction(FbIcon("help-about"), tr("&About"), this);
  411. act->setStatusTip(tr("Show the application's About box"));
  412. connect(act, SIGNAL(triggered()), this, SLOT(about()));
  413. menu->addAction(act);
  414. act = new QAction(tr("About &Qt"), this);
  415. act->setStatusTip(tr("Show the Qt library's About box"));
  416. connect(act, SIGNAL(triggered()), qApp, SLOT(aboutQt()));
  417. menu->addAction(act);
  418. }
  419. void FbMainWindow::openSettings()
  420. {
  421. FbSetupDlg dlg(this);
  422. dlg.exec();
  423. }
  424. void FbMainWindow::createTree()
  425. {
  426. if (textFrame && centralWidget() == textFrame) {
  427. dockTree = new FbDockWidget(tr("Contents"), this);
  428. dockTree->setWidget(new FbTreeWidget(textFrame->view(), this));
  429. connect(dockTree, SIGNAL(visibilityChanged(bool)), actionContents, SLOT(setChecked(bool)));
  430. connect(dockTree, SIGNAL(destroyed()), SLOT(treeDestroyed()));
  431. addDockWidget(Qt::LeftDockWidgetArea, dockTree);
  432. }
  433. }
  434. void FbMainWindow::createImgs()
  435. {
  436. if (textFrame && centralWidget() == textFrame) {
  437. dockImgs = new FbDockWidget(tr("Pictures"), this);
  438. dockImgs->setWidget(new FbListWidget(textFrame->view(), this));
  439. connect(dockImgs, SIGNAL(visibilityChanged(bool)), actionPictures, SLOT(setChecked(bool)));
  440. connect(dockImgs, SIGNAL(destroyed()), SLOT(imgsDestroyed()));
  441. addDockWidget(Qt::RightDockWidgetArea, dockImgs);
  442. }
  443. }
  444. void FbMainWindow::selectionChanged()
  445. {
  446. FbTextEdit *view = textFrame->view();
  447. actionCut->setEnabled(view->actionEnabled(QWebPage::Cut));
  448. actionCopy->setEnabled(view->actionEnabled(QWebPage::Copy));
  449. statusBar()->showMessage(view->page()->status());
  450. }
  451. void FbMainWindow::canUndoChanged(bool canUndo)
  452. {
  453. actionUndo->setEnabled(canUndo);
  454. }
  455. void FbMainWindow::canRedoChanged(bool canRedo)
  456. {
  457. actionRedo->setEnabled(canRedo);
  458. }
  459. void FbMainWindow::undoChanged()
  460. {
  461. actionUndo->setEnabled(textFrame->view()->actionEnabled(QWebPage::Undo));
  462. }
  463. void FbMainWindow::redoChanged()
  464. {
  465. actionRedo->setEnabled(textFrame->view()->actionEnabled(QWebPage::Redo));
  466. }
  467. void FbMainWindow::createStatusBar()
  468. {
  469. statusBar()->showMessage(tr("Ready"));
  470. }
  471. void FbMainWindow::readSettings()
  472. {
  473. QSettings settings;
  474. QPoint pos = settings.value("pos", QPoint(200, 200)).toPoint();
  475. QSize size = settings.value("size", QSize(400, 400)).toSize();
  476. move(pos);
  477. resize(size);
  478. }
  479. void FbMainWindow::writeSettings()
  480. {
  481. QSettings settings;
  482. settings.setValue("pos", pos());
  483. settings.setValue("size", size());
  484. }
  485. bool FbMainWindow::maybeSave()
  486. {
  487. if (textFrame && textFrame->view()->isModified()) {
  488. QMessageBox::StandardButton ret;
  489. ret = QMessageBox::warning(this, qApp->applicationName(),
  490. tr("The document has been modified. Do you want to save your changes?"),
  491. QMessageBox::Save | QMessageBox::Discard
  492. | QMessageBox::Cancel);
  493. if (ret == QMessageBox::Save)
  494. return fileSave();
  495. else if (ret == QMessageBox::Cancel)
  496. return false;
  497. }
  498. return true;
  499. }
  500. bool FbMainWindow::saveFile(const QString &fileName, const QString &codec)
  501. {
  502. QFile file(fileName);
  503. if (!file.open(QFile::WriteOnly | QFile::Text)) {
  504. QMessageBox::warning(this, qApp->applicationName(), tr("Cannot write file %1: %2.").arg(fileName).arg(file.errorString()));
  505. return false;
  506. }
  507. if (textFrame) {
  508. isSwitched = false;
  509. textFrame->view()->save(&file, codec);
  510. setCurrentFile(fileName);
  511. return true;
  512. }
  513. if (codeEdit) {
  514. QTextStream out(&file);
  515. out << codeEdit->toPlainText();
  516. setCurrentFile(fileName);
  517. return true;
  518. }
  519. return false;
  520. }
  521. void FbMainWindow::setCurrentFile(const QString &filename)
  522. {
  523. if (filename.isEmpty()) {
  524. static int sequenceNumber = 1;
  525. curFile = QString("book%1.fb2").arg(sequenceNumber++);
  526. } else {
  527. QFileInfo info = filename;
  528. curFile = info.canonicalFilePath();
  529. }
  530. setWindowFilePath(curFile);
  531. setModified(false);
  532. }
  533. QString FbMainWindow::appTitle() const
  534. {
  535. return QString(" - ") += qApp->applicationName() += QString(" ") += qApp->applicationVersion();
  536. }
  537. FbMainWindow *FbMainWindow::findFbMainWindow(const QString &fileName)
  538. {
  539. QString canonicalFilePath = QFileInfo(fileName).canonicalFilePath();
  540. foreach (QWidget *widget, qApp->topLevelWidgets()) {
  541. FbMainWindow *mainWin = qobject_cast<FbMainWindow *>(widget);
  542. if (mainWin && mainWin->curFile == canonicalFilePath)
  543. return mainWin;
  544. }
  545. return 0;
  546. }
  547. void FbMainWindow::checkScintillaUndo()
  548. {
  549. if (!codeEdit) return;
  550. actionUndo->setEnabled(codeEdit->isUndoAvailable());
  551. actionRedo->setEnabled(codeEdit->isRedoAvailable());
  552. }
  553. void FbMainWindow::createTextToolbar()
  554. {
  555. FbTextEdit * textEdit = textFrame->view();
  556. FbTextPage * textPage = textEdit->page();
  557. connect(textPage->undoStack(), SIGNAL(cleanChanged(bool)), SLOT(cleanChanged(bool)));
  558. connect(textPage->undoStack(), SIGNAL(canUndoChanged(bool)), SLOT(canUndoChanged(bool)));
  559. connect(textPage->undoStack(), SIGNAL(canRedoChanged(bool)), SLOT(canRedoChanged(bool)));
  560. connect(textPage, SIGNAL(selectionChanged()), SLOT(selectionChanged()));
  561. connect(actionUndo, SIGNAL(triggered()), textEdit->pageAction(QWebPage::Undo), SIGNAL(triggered()));
  562. connect(actionRedo, SIGNAL(triggered()), textEdit->pageAction(QWebPage::Redo), SIGNAL(triggered()));
  563. connect(actionCut, SIGNAL(triggered()), textEdit->pageAction(QWebPage::Cut), SIGNAL(triggered()));
  564. connect(actionCopy, SIGNAL(triggered()), textEdit->pageAction(QWebPage::Copy), SIGNAL(triggered()));
  565. connect(actionPaste, SIGNAL(triggered()), textEdit->pageAction(QWebPage::Paste), SIGNAL(triggered()));
  566. connect(actionPasteText, SIGNAL(triggered()), textEdit->pageAction(QWebPage::PasteAndMatchStyle), SIGNAL(triggered()));
  567. connect(actionClearFormat, SIGNAL(triggered()), textEdit->pageAction(QWebPage::RemoveFormat), SIGNAL(triggered()));
  568. connect(actionTextBold, SIGNAL(triggered()), textEdit->pageAction(QWebPage::ToggleBold), SIGNAL(triggered()));
  569. connect(actionTextItalic, SIGNAL(triggered()), textEdit->pageAction(QWebPage::ToggleItalic), SIGNAL(triggered()));
  570. connect(actionTextStrike, SIGNAL(triggered()), textEdit->pageAction(QWebPage::ToggleStrikethrough), SIGNAL(triggered()));
  571. connect(actionTextSub, SIGNAL(triggered()), textEdit->pageAction(QWebPage::ToggleSubscript), SIGNAL(triggered()));
  572. connect(actionTextSup, SIGNAL(triggered()), textEdit->pageAction(QWebPage::ToggleSuperscript), SIGNAL(triggered()));
  573. connect(textEdit->pageAction(QWebPage::RemoveFormat), SIGNAL(changed()), actionClearFormat, SLOT(updateEnabled()));
  574. connect(textEdit->pageAction(QWebPage::ToggleBold), SIGNAL(changed()), actionTextBold, SLOT(updateChecked()));
  575. connect(textEdit->pageAction(QWebPage::ToggleItalic), SIGNAL(changed()), actionTextItalic, SLOT(updateChecked()));
  576. connect(textEdit->pageAction(QWebPage::ToggleStrikethrough), SIGNAL(changed()), actionTextStrike, SLOT(updateChecked()));
  577. connect(textEdit->pageAction(QWebPage::ToggleSubscript), SIGNAL(changed()), actionTextSub, SLOT(updateChecked()));
  578. connect(textEdit->pageAction(QWebPage::ToggleSuperscript), SIGNAL(changed()), actionTextSup, SLOT(updateChecked()));
  579. connect(actionFind, SIGNAL(triggered()), textEdit, SLOT(find()));
  580. connect(actionImage, SIGNAL(triggered()), textEdit, SLOT(insertImage()));
  581. connect(actionNote, SIGNAL(triggered()), textEdit, SLOT(insertNote()));
  582. connect(actionLink, SIGNAL(triggered()), textEdit, SLOT(insertLink()));
  583. connect(actionTitle, SIGNAL(triggered()), textPage, SLOT(insertTitle()));
  584. connect(actionAnnot, SIGNAL(triggered()), textPage, SLOT(insertAnnot()));
  585. connect(actionAuthor, SIGNAL(triggered()), textPage, SLOT(insertAuthor()));
  586. connect(actionEpigraph, SIGNAL(triggered()), textPage, SLOT(insertEpigraph()));
  587. connect(actionSubtitle, SIGNAL(triggered()), textPage, SLOT(insertSubtitle()));
  588. connect(actionSection, SIGNAL(triggered()), textPage, SLOT(insertSection()));
  589. connect(actionStanza, SIGNAL(triggered()), textPage, SLOT(insertStanza()));
  590. connect(actionPoem, SIGNAL(triggered()), textPage, SLOT(insertPoem()));
  591. connect(actionDate, SIGNAL(triggered()), textPage, SLOT(insertDate()));
  592. connect(actionBody, SIGNAL(triggered()), textPage, SLOT(insertBody()));
  593. connect(actionParaSeparator, SIGNAL(triggered()), textEdit->pageAction(QWebPage::InsertParagraphSeparator), SIGNAL(triggered()));
  594. connect(actionLineSeparator, SIGNAL(triggered()), textEdit->pageAction(QWebPage::InsertLineSeparator), SIGNAL(triggered()));
  595. connect(actionSectionAdd, SIGNAL(triggered()), textPage, SLOT(createSection()));
  596. connect(actionSectionDel, SIGNAL(triggered()), textPage, SLOT(deleteSection()));
  597. connect(actionTextTitle, SIGNAL(triggered()), textPage, SLOT(createTitle()));
  598. connect(actionZoomIn, SIGNAL(triggered()), textEdit, SLOT(zoomIn()));
  599. connect(actionZoomOut, SIGNAL(triggered()), textEdit, SLOT(zoomOut()));
  600. connect(actionZoomReset, SIGNAL(triggered()), textEdit, SLOT(zoomReset()));
  601. FB2DELETE(toolEdit);
  602. QToolBar *tool = toolEdit = addToolBar(tr("Edit"));
  603. tool->setMovable(false);
  604. tool->addSeparator();
  605. textEdit->addTools(tool);
  606. tool->addSeparator();
  607. tool->addAction(actionSectionAdd);
  608. tool->addAction(actionSectionDel);
  609. tool->addAction(actionTextTitle);
  610. tool->addSeparator();
  611. tool->addAction(actionImage);
  612. tool->addAction(actionNote);
  613. tool->addAction(actionLink);
  614. tool->addAction(actionSection);
  615. }
  616. void FbMainWindow::viewText()
  617. {
  618. if (textFrame && centralWidget() == textFrame) return;
  619. if (textFrame) textFrame->hideInspector();
  620. bool load = false;
  621. QString xml;
  622. if (codeEdit) {
  623. xml = codeEdit->text();
  624. isSwitched = true;
  625. load = true;
  626. }
  627. FB2DELETE(codeEdit);
  628. FB2DELETE(headTree);
  629. if (textFrame) {
  630. createTextToolbar();
  631. } else {
  632. textFrame = new FbTextFrame(this, actionInspect);
  633. }
  634. setCentralWidget(textFrame);
  635. textFrame->view()->setFocus();
  636. viewTree();
  637. FbTextEdit *textEdit = textFrame->view();
  638. connect(textEdit, SIGNAL(loadFinished(bool)), SLOT(createTextToolbar()));
  639. connect(textEdit->pageAction(QWebPage::Undo), SIGNAL(changed()), SLOT(undoChanged()));
  640. connect(textEdit->pageAction(QWebPage::Redo), SIGNAL(changed()), SLOT(redoChanged()));
  641. if (load) textFrame->view()->load(curFile, xml);
  642. actionContents->setEnabled(true);
  643. actionPictures->setEnabled(true);
  644. actionInspect->setEnabled(true);
  645. }
  646. void FbMainWindow::viewHead()
  647. {
  648. if (headTree && centralWidget() == headTree) return;
  649. if (textFrame) textFrame->hideInspector();
  650. QString xml;
  651. if (codeEdit) xml = codeEdit->text();
  652. FB2DELETE(dockTree);
  653. FB2DELETE(dockImgs);
  654. FB2DELETE(codeEdit);
  655. FB2DELETE(toolEdit);
  656. if (!textFrame) {
  657. textFrame = new FbTextFrame(this, actionInspect);
  658. }
  659. if (!headTree) {
  660. headTree = new FbHeadView(textFrame->view(), this);
  661. connect(headTree, SIGNAL(status(QString)), this, SLOT(status(QString)));
  662. }
  663. this->setFocus();
  664. textFrame->setParent(NULL);
  665. setCentralWidget(headTree);
  666. textFrame->setParent(this);
  667. headTree->updateTree();
  668. headTree->setFocus();
  669. if (!xml.isEmpty()) textFrame->view()->load(curFile, xml);
  670. if (textFrame) {
  671. actionUndo->disconnect();
  672. actionRedo->disconnect();
  673. actionCut->disconnect();
  674. actionCopy->disconnect();
  675. actionPaste->disconnect();
  676. actionTextBold->disconnect();
  677. actionTextItalic->disconnect();
  678. actionTextStrike->disconnect();
  679. actionTextSub->disconnect();
  680. actionTextSup->disconnect();
  681. }
  682. FB2DELETE(toolEdit);
  683. toolEdit = addToolBar(tr("Edit"));
  684. headTree->initToolbar(*toolEdit);
  685. toolEdit->addSeparator();
  686. toolEdit->setMovable(false);
  687. actionContents->setEnabled(false);
  688. actionPictures->setEnabled(false);
  689. actionInspect->setEnabled(true);
  690. }
  691. void FbMainWindow::viewCode()
  692. {
  693. if (codeEdit && centralWidget() == codeEdit) return;
  694. bool load = false;
  695. QByteArray xml;
  696. QString html;
  697. if (textFrame) {
  698. textFrame->view()->save(&xml);
  699. html = textFrame->view()->page()->mainFrame()->toHtml();
  700. isSwitched = true;
  701. load = true;
  702. }
  703. FB2DELETE(textFrame);
  704. FB2DELETE(dockTree);
  705. FB2DELETE(dockImgs);
  706. FB2DELETE(headTree);
  707. if (!codeEdit) {
  708. codeEdit = new FbCodeEdit;
  709. }
  710. if (load) codeEdit->load(xml);
  711. codeEdit->setPlainText(html);
  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. bool load = false;
  750. QString html = textFrame->view()->page()->mainFrame()->toHtml();
  751. isSwitched = true;
  752. load = true;
  753. FB2DELETE(textFrame);
  754. FB2DELETE(dockTree);
  755. FB2DELETE(dockImgs);
  756. FB2DELETE(headTree);
  757. if (!codeEdit) {
  758. codeEdit = new FbCodeEdit;
  759. }
  760. codeEdit->setPlainText(html);
  761. setCentralWidget(codeEdit);
  762. codeEdit->setFocus();
  763. FB2DELETE(toolEdit);
  764. QToolBar *tool = toolEdit = addToolBar(tr("Edit"));
  765. tool->addSeparator();
  766. tool->addAction(actionUndo);
  767. tool->addAction(actionRedo);
  768. tool->addSeparator();
  769. tool->addAction(actionCut);
  770. tool->addAction(actionCopy);
  771. tool->addAction(actionPaste);
  772. tool->addSeparator();
  773. tool->addAction(actionZoomIn);
  774. tool->addAction(actionZoomOut);
  775. tool->addAction(actionZoomReset);
  776. tool->setMovable(false);
  777. connect(codeEdit, SIGNAL(textChanged()), this, SLOT(documentWasModified()));
  778. connect(codeEdit, SIGNAL(textChanged()), this, SLOT(checkScintillaUndo()));
  779. connect(codeEdit, SIGNAL(copyAvailable(bool)), actionCut, SLOT(setEnabled(bool)));
  780. connect(codeEdit, SIGNAL(copyAvailable(bool)), actionCopy, SLOT(setEnabled(bool)));
  781. connect(actionUndo, SIGNAL(triggered()), codeEdit, SLOT(undo()));
  782. connect(actionRedo, SIGNAL(triggered()), codeEdit, SLOT(redo()));
  783. connect(actionCut, SIGNAL(triggered()), codeEdit, SLOT(cut()));
  784. connect(actionCopy, SIGNAL(triggered()), codeEdit, SLOT(copy()));
  785. connect(actionPaste, SIGNAL(triggered()), codeEdit, SLOT(paste()));
  786. connect(actionFind, SIGNAL(triggered()), codeEdit, SLOT(find()));
  787. connect(actionZoomIn, SIGNAL(triggered()), codeEdit, SLOT(zoomIn()));
  788. connect(actionZoomOut, SIGNAL(triggered()), codeEdit, SLOT(zoomOut()));
  789. connect(actionZoomReset, SIGNAL(triggered()), codeEdit, SLOT(zoomReset()));
  790. actionContents->setEnabled(false);
  791. actionPictures->setEnabled(false);
  792. actionInspect->setEnabled(false);
  793. }
  794. void FbMainWindow::viewTree()
  795. {
  796. if (dockTree) dockTree->deleteLater(); else createTree();
  797. }
  798. void FbMainWindow::viewImgs()
  799. {
  800. if (dockImgs) dockImgs->deleteLater(); else createImgs();
  801. }
  802. void FbMainWindow::clipboardDataChanged()
  803. {
  804. if (const QMimeData *md = QApplication::clipboard()->mimeData()) {
  805. actionPaste->setEnabled(md->hasText());
  806. actionPasteText->setEnabled(md->hasText());
  807. }
  808. }
  809. void FbMainWindow::status(const QString &text)
  810. {
  811. statusBar()->showMessage(text);
  812. }