├── .gitignore ├── .gitmodules ├── LICENSE ├── NOTES.txt ├── Plotline.pro ├── README.markdown ├── config └── config.xml ├── doc ├── .gitignore ├── doc.pro ├── images │ └── qt-run-button.png ├── index.markdown └── user │ ├── character.markdown │ ├── images │ └── plotline-novel.png │ ├── index.markdown │ ├── novel.markdown │ └── plotline.markdown ├── packages └── me.rngr.plotline │ └── meta │ ├── installscript.qs │ ├── license.txt │ ├── package.xml │ └── page.ui ├── plotline.sh ├── src ├── 3rdparty │ └── 3rdparty.pro ├── Plotline.pro.user ├── app │ ├── app.pro │ ├── chapterfilter.cpp │ ├── chapterfilter.h │ ├── chaptermodel.cpp │ ├── chaptermodel.h │ ├── chapterreorderdialog.cpp │ ├── chapterreorderdialog.h │ ├── chapterreorderdialog.ui │ ├── chaptersframe.cpp │ ├── chaptersframe.h │ ├── chaptersframe.ui │ ├── characterframe.cpp │ ├── characterframe.h │ ├── characterframe.ui │ ├── characterhighlighter.cpp │ ├── characterhighlighter.h │ ├── characteritemmodel.cpp │ ├── characteritemmodel.h │ ├── fullscreeneditor.cpp │ ├── fullscreeneditor.h │ ├── fullscreeneditor.ui │ ├── main.cpp │ ├── mainwindow.cpp │ ├── mainwindow.h │ ├── mainwindow.ui │ ├── markuphighlighter.cpp │ ├── markuphighlighter.h │ ├── modelcheckbox.cpp │ ├── modelcheckbox.h │ ├── novelframe.cpp │ ├── novelframe.h │ ├── novelframe.ui │ ├── plotframe.cpp │ ├── plotframe.h │ ├── plotframe.ui │ ├── plotlineappframe.cpp │ ├── plotlineappframe.h │ ├── plotlinedialog.cpp │ ├── plotlinedialog.h │ ├── plotlinedialog.ui │ ├── plotlineitemdelegate.cpp │ ├── plotlineitemdelegate.h │ ├── plotlineitemmodel.cpp │ ├── plotlineitemmodel.h │ ├── preferencesdialog.cpp │ ├── preferencesdialog.h │ ├── preferencesdialog.ui │ ├── publisherdialog.cpp │ ├── publisherdialog.h │ ├── publisherdialog.ui │ ├── revisiondialog.cpp │ ├── revisiondialog.h │ ├── revisiondialog.ui │ ├── savethread.cpp │ ├── savethread.h │ ├── scenefilter.cpp │ ├── scenefilter.h │ ├── sceneframe.cpp │ ├── sceneframe.h │ ├── sceneframe.ui │ ├── sceneitemmodel.cpp │ ├── sceneitemmodel.h │ ├── scenelistdialog.cpp │ ├── scenelistdialog.h │ ├── scenelistdialog.ui │ ├── styleproxy.cpp │ ├── styleproxy.h │ ├── styleproxyparser.cpp │ └── styleproxyparser.h ├── data │ ├── character-worthiness.json │ ├── novel-overview.json │ ├── novel-part-1.json │ ├── novel-part-2.json │ └── novel-part-3.json ├── lib │ ├── author.cpp │ ├── author.h │ ├── chapter.cpp │ ├── chapter.h │ ├── character.cpp │ ├── character.h │ ├── characterparser.cpp │ ├── characterparser.h │ ├── completable.cpp │ ├── completable.h │ ├── lib.pro │ ├── novel.cpp │ ├── novel.h │ ├── plotline.cpp │ ├── plotline.h │ ├── revision.cpp │ ├── revision.h │ ├── scene.cpp │ ├── scene.h │ ├── serializable.cpp │ ├── serializable.h │ ├── utils.cpp │ └── utils.h ├── res │ ├── images │ │ ├── .directory │ │ ├── 45834457236.svg │ │ ├── ATTRIBUTIONS.txt │ │ ├── application-exit.png │ │ ├── book.png │ │ ├── document-new.png │ │ ├── document-open-folder.png │ │ ├── document-open.png │ │ ├── document-save-as.png │ │ ├── document-save.png │ │ ├── edit-clear-locationbar-rtl.png │ │ ├── emblem-favorite.png │ │ ├── fill-color.png │ │ ├── format-list-ordered.png │ │ ├── gtk-edit.svg │ │ ├── list-add-plotline.png │ │ ├── list-add-user.png │ │ ├── list-add.png │ │ ├── list-remove-plotline.png │ │ ├── list-remove-user.png │ │ ├── list-remove.png │ │ ├── plotline-launcher.svg │ │ ├── revision.svg │ │ ├── user-archive.png │ │ ├── user-identity.png │ │ ├── user-properties.png │ │ └── utilities-file-archiver.png │ ├── plotline.qrc │ └── styles │ │ ├── default-dark.json │ │ └── default.json └── src.pro └── test ├── test.pro └── unit ├── fixtures ├── chapter-deserialize.json ├── character-deserialize.json ├── novel-deserialize.json ├── revision-bug-test.json └── revision-deserialize.json ├── main.cpp ├── testcharacterparser.cpp ├── testcharacterparser.h ├── tst_chapter.cpp ├── tst_chapter.h ├── tst_character.cpp ├── tst_character.h ├── tst_novel.cpp ├── tst_novel.h ├── tst_plotline.cpp ├── tst_plotline.h ├── tst_revision.cpp ├── tst_revision.h ├── tst_scene.cpp ├── tst_scene.h ├── tst_utils.cpp ├── tst_utils.h └── unit.pro /.gitignore: -------------------------------------------------------------------------------- 1 | *.pro.user 2 | *.o 3 | test/test 4 | *.autosave 5 | qt.conf 6 | moc_* 7 | ui_*.[chp]* 8 | *~ 9 | Makefile* 10 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "src/3rdparty/hunspell"] 2 | path = src/3rdparty/hunspell 3 | url = https://github.com/hunspell/hunspell.git 4 | [submodule "src/3rdparty/discount"] 5 | path = src/3rdparty/discount 6 | url = https://github.com/Orc/discount.git 7 | -------------------------------------------------------------------------------- /NOTES.txt: -------------------------------------------------------------------------------- 1 | 2 | To-Do List for v. 0.1a 3 | ======================= 4 | 5 | Bugs 6 | ---- 7 | ! drag & drop feature for character list can't drop. 8 | ! Sometimes adding a plotline will override another. 9 | ! FIXED Adding a scene with a plotline selected will not assign the scene 10 | to the plotline (in fact, the scene won't even be selected!). 11 | ! Plotline dialog character list doesn't filter as expected. 12 | ! FIXED Opening a novel will result in the save state automatically set to 13 | false. possibly has to do with signals being emitted. 14 | ! chapter editor doesn't break on a word. 15 | !! CRITICAL Saving before exit: mainWindow is destroyed before thread 16 | can save. 17 | 18 | Features 19 | -------- 20 | + Delete functionality for 21 | + DONE Characcter 22 | + DONE Scene 23 | + Chapter 24 | + Archive functionality (might need different list structure) for... 25 | + Character 26 | + Scene 27 | + Plotline 28 | + Chapter 29 | + DONE Publish novel to flat markdown (other formats will come later). 30 | + Character name dropdown for scene frame 31 | -------------------------------------------------------------------------------- /Plotline.pro: -------------------------------------------------------------------------------- 1 | ###################################################################### 2 | # Automatically generated by qmake (3.0) Tue Jan 19 19:04:32 2016 3 | ###################################################################### 4 | 5 | TEMPLATE = subdirs 6 | 7 | CONFIG += ordered 8 | 9 | SUBDIRS = src \ 10 | doc 11 | 12 | CONFIG(debug, debug|release): SUBDIRS += test 13 | 14 | OTHER_FILES = \ 15 | README.markdown 16 | 17 | # Copy the bash executable. 18 | 19 | unix { 20 | copydata.commands = $(COPY_DIR) "$$PWD/plotline.sh" "$$OUT_PWD" 21 | first.depends = $(first) copydata 22 | export(first.depends) 23 | export(copydata.commands) 24 | QMAKE_EXTRA_TARGETS += first copydata 25 | } 26 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | # Plotline 2 | 3 | Plotline is an application to assist in the creation of a novel. While this does 4 | not fully replace the old-school P&P (pencil & paper) method, it is nice to have 5 | everything in one place. Some of the [planned] features include: 6 | 7 | - Planning the novel plot arc, form inciting incident to the climax. 8 | - Adding characters, with prompts for character biographies. 9 | - Creating plotlines for the novel, and assigning characters as part of the 10 | plotlines. 11 | - Adding scenes to the novel to plan out exactly how the novel will work out. 12 | - Best feature: a full-fleged editor, with markdown syntax highlighting support 13 | and a distraction-free mode. 14 | - Better feature: binding the novel content into a various format, ebook or 15 | otherwise. 16 | 17 | ## Current State 18 | 19 | The back-end works fine and passes all tests. If someone wants to go for more 20 | test coverage I wouldn't complain. 21 | 22 | After a revamp to refactor the code into the seperate tab frames I've been 23 | reconnecting the signals. The basic theory looks like this: 24 | 25 | [Main App, *novel] 26 | | 27 | +---------+--------+-----------+-----------+ 28 | | | | | | 29 | Novel Character Plotline Scene Chapter 30 | Frame Frame Frame frame frame 31 | [*novel] [*novel] [*novel] [*novel] [*novel] 32 | 33 | Each frame will contain a pointer to the currently-opened novel stored in the 34 | pointer `novel`. Each frame corresponds to the named backend class, so 35 | `NovelFrame` is the view for the `Novel` class, `CharacterFrame` is the view for 36 | the `Character` class and so-on. 37 | 38 | Each frame is a subclass `PlotlineAppFrame`, which inherits the signal 39 | `novelModified()` (which will notify the `MainWindow` when a change occurs) 40 | and the slots `noNovelLoad()` (which will update the frame fields when a new 41 | novel is loaded) and `onNovelNew()` (which will clear the frame's fields). 42 | 43 | ## Getting a Development Environment Set Up 44 | 45 | ### Getting Qt 46 | 47 | Plotline was developed in Qt, a C++ applicaiton framework. If you're new to Qt, 48 | now is the best time to learn! 49 | 50 | Side note: if you're new to Qt, Qt doesn't use a conventional build system like 51 | CMake or Make. Instead, it uses QMake, which generates the Make files and meta 52 | objects. So if you try to load the project in KDevelop or Eclipse or try to 53 | compile on the command line it might get kind of frustrating. QtCreator was used 54 | to develop this application, and that's what I would recommend. 55 | 56 | Okay, back to set-up. 57 | 58 | First thing's first, you'll need to install Qt5. Either install it with your 59 | distribution's package manager (in the case of Linux) or download from 60 | [Qt.io](http://www.qt.io/) and install that way. 61 | 62 | ### Getting the source. 63 | 64 | Installing Qt should have also installed QtCreator. Open QtCreator and go to 65 | **File > New File or Project > Import Project > Git Clone**. In *Repository* 66 | enter `https://github.com/freckles-the-pirate/plotline.git`. Check 67 | **Recursive** --this will import submodules. If the current version doesn't 68 | have submodules, most likely others will. 69 | 70 | #### Command line 71 | 72 | Alternatively, if you're a command-line mastro, you can git clone the 73 | repository: 74 | 75 | $ git clone https://github.com/freckles-the-pirate/plotline.git 76 | $ cd plotline 77 | $ git submodule init 78 | $ git submdoule sync 79 | 80 | And then open the project with **File > Open File or Project**, and open the 81 | Project (`.pro`) file. 82 | 83 | 84 | ### Important!!! Set the Library path 85 | 86 | One more thing needs to be attended to. The core application code (in **lib** ) 87 | is a static library in order to allow linking with either the **test** 88 | subproject or the **app** subproject. 89 | 90 | To add this path to the library, go to the **Projects** tab along the lefthand 91 | side of QtDesigner. Up at the top click on the **Run** tab. Expand the 92 | **Environment** section and modify the `LD_LIBRARY_PATH` variable to append the 93 | path `../lib/`. For example, if the original value was 94 | 95 | /usr/lib/x86_64-linux-gnu/ 96 | 97 | Modify the value to look like this: 98 | 99 | /usr/lib/x86_64-linux-gnu/:../lib/ 100 | 101 | Remember, `:` means "append" in BASH. ;-) 102 | 103 | Do the same for the other subprojects by selecting the run button (that looks 104 | like this: ![Qt Run Button](./doc/images/qt-run-button.png) ) 105 | -------------------------------------------------------------------------------- /config/config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Plotline 4 | 0.0.1a 5 | Plotline Installer 6 | Renegade Engineer 7 | Authoring 8 | @HomeDir@/Plotline 9 | 10 | -------------------------------------------------------------------------------- /doc/.gitignore: -------------------------------------------------------------------------------- 1 | # This file is used to ignore files which are generated 2 | # ---------------------------------------------------------------------------- 3 | 4 | *~ 5 | *.autosave 6 | *.a 7 | *.core 8 | *.moc 9 | *.o 10 | *.obj 11 | *.orig 12 | *.rej 13 | *.so 14 | *.so.* 15 | *_pch.h.cpp 16 | *_resource.rc 17 | *.qm 18 | .#* 19 | *.*# 20 | core 21 | !core/ 22 | tags 23 | .DS_Store 24 | *.debug 25 | Makefile* 26 | *.prl 27 | *.app 28 | moc_*.cpp 29 | ui_*.h 30 | qrc_*.cpp 31 | Thumbs.db 32 | *.res 33 | *.rc 34 | /.qmake.cache 35 | /.qmake.stash 36 | 37 | # qtcreator generated files 38 | *.pro.user* 39 | 40 | # xemacs temporary files 41 | *.flc 42 | 43 | # Vim temporary files 44 | .*.swp 45 | 46 | # Visual Studio generated files 47 | *.ib_pdb_index 48 | *.idb 49 | *.ilk 50 | *.pdb 51 | *.sln 52 | *.suo 53 | *.vcproj 54 | *vcproj.*.*.user 55 | *.ncb 56 | *.sdf 57 | *.opensdf 58 | *.vcxproj 59 | *vcxproj.* 60 | 61 | # MinGW generated files 62 | *.Debug 63 | *.Release 64 | 65 | # Python byte code 66 | *.pyc 67 | 68 | # Binaries 69 | # -------- 70 | *.dll 71 | *.exe 72 | 73 | 74 | -------------------------------------------------------------------------------- /doc/doc.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = aux 2 | 3 | DISTFILES += \ 4 | index.markdown \ 5 | user/index.markdown \ 6 | user/novel.markdown \ 7 | user/plotline.markdown \ 8 | user/scene.markdown \ 9 | user/character.markdown 10 | -------------------------------------------------------------------------------- /doc/images/qt-run-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/src-r-r/plotline/726ab94d379ca21206f6979d2ebb92be83149593/doc/images/qt-run-button.png -------------------------------------------------------------------------------- /doc/index.markdown: -------------------------------------------------------------------------------- 1 | # Plotline 2 | 3 | ## Setting Up for Development 4 | 5 | Plotline is written in Qt, and, therefore, it's really easy to set up in 6 | QtCreator. If you're new to QtCreator, now's the perfect time to get yourself 7 | acquainted! Head over to [qt.io](https://qt.io) to get set up with Qt and 8 | QtCreator. 9 | 10 | Once you're set up, clone the repository... 11 | 12 | $ git clone {{ plotline-repository }} 13 | 14 | ...and initialize the submodules: 15 | 16 | $ git submodule init 17 | 18 | Three other submodules will be cloned: 19 | 20 | - CuteMarkEd: heavily used for the Markdown editor. 21 | - discount: library used to Markdown parsing (dependency of CuteMarkEd) 22 | - hunspell: spell-checker library. 23 | 24 | ## IMPORTANT!! BUILD EACH SUBMODULE!! 25 | 26 | I'll be working on automatically building each submodule by using QMake. Until 27 | then each submodule should be built before building Plotline. 28 | 29 | $ cd src/3rdparty/discount/ 30 | $ ./configure && make; # don't run install (you can, but there's no point) 31 | $ cd ../hunspell/ 32 | $ ./configure && make 33 | 34 | Since CuteMarkEd uses the QMake build system it will be handled by Plotline's 35 | QMake. 36 | -------------------------------------------------------------------------------- /doc/user/character.markdown: -------------------------------------------------------------------------------- 1 | # Characters 2 | 3 | ## Character List 4 | 5 | The character list can be used for listing characters used in your novel. The 6 | overall name is used as the list entry. Characters can be added, removed, 7 | archived, or edited. 8 | 9 | ## Character Details 10 | 11 | To the right of the character list is the character. The following fields are 12 | used to add details to the character: 13 | 14 | ### Name 15 | 16 | Name of your character. This will be the display name throughout the rest of 17 | your novel. 18 | 19 | ### Label 20 | 21 | The label is used to easily identify your characters in writing scenes or 22 | plotlines. The label will be automatically filled in based on the **Name** and 23 | is calculated based on the first two letters of each character's name. For 24 | example, for the name `John Doe` Plotline will fill in the **Label** as `JoDo`. 25 | Of course, this can always be changed. 26 | 27 | To refer to the character by label (the preferred method), precede the label 28 | with an "at" (`@`) character. So to refer to the character *John Doe* in the 29 | [Scenes](./scene.markdown) or [Plotlines](./plotline.markdown) type `@JoDo`. 30 | See the [Scenes](./scene.markdown) documentation for more information. 31 | 32 | ### Nick Name 33 | 34 | Every character has a nick name now, right? This is for your records only. 35 | 36 | ### Color 37 | 38 | The developer of Plotline believes everything is better in color! That's why 39 | you have the ability to choose a color for your character. 40 | 41 | To choose a color, click the **Choose Color** button. If you do not want a 42 | color for your character, click the **Clear Color** button. 43 | 44 | Besides being the 45 | background color for the character in the **Character List** the color will 46 | also be the background color when a *character label* is detected in the 47 | [Scenes](./scene.markdown) or [Plotlines](./plotline.markdown). 48 | 49 | ### Headshot 50 | 51 | A headshot is useful to help visualize a person and make the character more 52 | believable. 53 | 54 | A headshot can be selected from your hard drive. To select a headshot, click 55 | the **Choose Headshot** button to select a headhshot. The headshot is saved 56 | within the `.pln` file itself and cannot be exported from the application. 57 | 58 | ### Add Character 59 | 60 | To add a character, click the **Add Character** button. A new entry will be 61 | displayed in the list and the **Character Details** on the right will be 62 | enabled, allowing you to modify the character's details. 63 | 64 | ### Remove Character 65 | 66 | To remove a character, select the character and click **Remove Character**. A 67 | dialog will open asking if you would like to really remove the character 68 | (annoying, I know, but we don't want any lawsuits). Click **Delete** to remove 69 | the character or **Cancel** to keep the character. -------------------------------------------------------------------------------- /doc/user/images/plotline-novel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/src-r-r/plotline/726ab94d379ca21206f6979d2ebb92be83149593/doc/user/images/plotline-novel.png -------------------------------------------------------------------------------- /doc/user/index.markdown: -------------------------------------------------------------------------------- 1 | # Plotline Usage 2 | 3 | ## Why use all these tabs, anyway? Can't I just write? 4 | 5 | I know what it's like. It's really tempting to skip all the steps in writing a 6 | novel. "I have the most epic climax in mind! I just want to get it on paper!" 7 | 8 | Alright, I understand. All writers do. However, I'd highly recommend going 9 | through each aspect of your novel and flesh out exactly how it's going to work 10 | out. This is especially handy if you're writing something with a non-linear 11 | plot or complex storylines. 12 | 13 | In fact I'd even recommend using the old pencil-and-paper method before you 14 | even open Plotline! Plotline isn't intended to be a replacement for the work 15 | … It's only indended to be a digital conversion. 16 | 17 | But, assuming you've been a good little writer and done everything correctly, 18 | here's how to use plotline. 19 | 20 | ## The Overall View 21 | 22 | ### Novel 23 | 24 | For the app's intents and purposes, the project you're working on is called a 25 | **novel** (for obvious reasons). 26 | 27 | The first tab in the notebook is used to get (you) the author thinking of what 28 | novel you're going to write. Of course, the title is important, but other 29 | aspects are just as important to think about, such as point of view and tense. 30 | 31 | ### Characters 32 | 33 | Have you ever read a novel only to find you couldn't get emotionally-invested 34 | in characters? One big reason this happens is because authors fail to flesh out 35 | characters--namely, the major characters. 36 | 37 | The *Characters* tab provides prompts to develop these deep characters. 38 | 39 | ### Plotline 40 | 41 | For novels that have a non-linear plotline (meaning 2 or more plotlines 42 | involved in the storyline), this view is helpful for getting an idea what will 43 | happen with teach plotline, and which characters are involved. 44 | 45 | ### Scene 46 | 47 | A scene is an event that occurs within a plotline. The scene tab allows you to 48 | specify the headline (a quick description of what's going on) and the action 49 | (a more detailed, step-by-step description of what's going on). 50 | 51 | ### Chapter 52 | 53 | Here you can actually write your novel. 54 | 55 | ## The Menu 56 | 57 | ### Novel > Revisions 58 | 59 | Add revisions to your novel. Each revision is exactly -------------------------------------------------------------------------------- /doc/user/novel.markdown: -------------------------------------------------------------------------------- 1 | # Novel Frame 2 | 3 | The *Novel Frame* is the place to store overall details on the novel being 4 | written. When [binding a novel](./binding.markdown), Plotline will pull details 5 | from this page. Here is a detailed look at each of the pages for the novel frame. 6 | 7 | The components to this frame are based heavily off 8 | [Annie Neugebauer's Novel Plotting Prompts](http://annieneugebauer.com/the-organized-writer-2/novel-plotting-worksheets/). 9 | 10 | ## Overview 11 | 12 | The overview is for the novel title, point of view (POV), and tense. Only the 13 | title will be used when publishing the novel; the POV, tense, and setting will 14 | not be published. 15 | 16 | ## Author 17 | 18 | Information on the author of the novel. While not required, highly recommended. 19 | All this information will be published when the novel is bound and can be added 20 | or omitted as desired. 21 | -------------------------------------------------------------------------------- /doc/user/plotline.markdown: -------------------------------------------------------------------------------- 1 | # Plotline 2 | 3 | This will mainly be used for non-linear stories (meaning, stories with more 4 | than one plotline), but can be useful for any type of story. 5 | 6 | ## Plotline Table 7 | 8 | Compared to the [Character Tab](./character.markdown) the plotline tab includes 9 | a table where the details are listed. Three fields--or columns--are displayed 10 | within the table and are available within the **Plotline Dialog** 11 | 12 | ### Brief 13 | 14 | A one-liner describing what happens with this plotline. Used for displaying in 15 | subsequent tabs. 16 | 17 | ### Synopsis 18 | 19 | A more detailed description of this scene. Used more for your reference. 20 | 21 | ### Characters Involved. 22 | 23 | Characters created in the [Character Tab](./character.markdown) will be 24 | displayed here in a comma-separated list. 25 | 26 | ## Actions 27 | 28 | The following actions can be performed on plotlines. 29 | 30 | ### Add Plotline 31 | 32 | On adding a plotline, the plotline dialog will be displayed to enter initial 33 | values. Upon clicking **OK** the dialog will be closed and the new plotline 34 | will be added to the plotline table. 35 | 36 | ### Edit Plotline 37 | 38 | To edit a plotline, either double-click on the plotline to edit, or select the 39 | plotline and click the **Edit Plotline** button. A **Plotline Dialog** will be 40 | displayed with the plotline to be edited. 41 | 42 | ### Delete Plotline. 43 | 44 | To delete a plotline select the plotline you'd like to delete and click the 45 | **Delete Plotline** button. A dialog will be displayed confirming you'd like to 46 | delete the plotline. Click **OK** to delete the plotline, **Cancel** if you do 47 | not want to delete it. The plotline will be removed from the table. 48 | 49 | ## Plotline Dialog 50 | 51 | This dialog will be displayed when either creating or modifying a plotline. The 52 | fields listed above will be displayed in the dialog in addition to two more 53 | fields: 54 | 55 | ### Color 56 | 57 | The plotline can be given a color. This color will be used for the background 58 | color in the **Plotline Table** as well as the background color for the scenes 59 | in the [Scenes Tab](./scenes.markdown). 60 | 61 | To assign a color click on the **Choose Color** button. To clear the color 62 | click the **Clear Color** button. 63 | 64 | ### Character Search Field 65 | 66 | Directly above the **Characters Involved** is a search field. To search by name 67 | simply type in the character's name. The checkboxes will be filtered 68 | dynamically. Users can also be filtered by label. Simply start the search with 69 | an "at sign" (`@`) to display results with that character label. To display all 70 | the characters again empty the search field. -------------------------------------------------------------------------------- /packages/me.rngr.plotline/meta/installscript.qs: -------------------------------------------------------------------------------- 1 | function Controller() 2 | { 3 | } 4 | -------------------------------------------------------------------------------- /packages/me.rngr.plotline/meta/package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Plotline 4 | 5 | Application to create, manage, and publish a novel. 6 | 7 | 1.0 8 | 2016-03-04 9 | me.rngr.plotline 10 | 11 | 12 | 13 | script 14 | 15 | 16 | page.ui 17 | 18 | 19 | -------------------------------------------------------------------------------- /packages/me.rngr.plotline/meta/page.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Page 4 | 5 | 6 | 7 | 0 8 | 0 9 | 400 10 | 300 11 | 12 | 13 | 14 | Dynamic page example 15 | 16 | 17 | 18 | 19 | 20 | Qt::AlignCenter 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /plotline.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Thanks to https://stackoverflow.com/a/246128 4 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 5 | 6 | LD_LIBRARY_PATH=$LD_LIBRARY_PATH:src/lib $DIR/src/app/plotline 7 | -------------------------------------------------------------------------------- /src/3rdparty/3rdparty.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = subdirs 2 | 3 | SUBDIRS += CuteMarkEd/3rdparty/ 4 | 5 | SOURCES += CuteMarkEd/app/markdowneditor.cpp \ 6 | CuteMarkEd/app/markdownhighlighter.cpp \ 7 | CuteMarkEd/app/highlightworkerthread.cpp 8 | 9 | HEADERS += CuteMarkEd/app/markdowneditor.h \ 10 | CuteMarkEd/app/markdownhighlighter.h \ 11 | CuteMarkEd/app/highlightworkerthread.h 12 | 13 | INCLUDEPATH += CuteMarkEd/3rdparty/hunspell/src/ 14 | 15 | # build order: 3rdparty -> libs -> app-static -> app & test 16 | libs.depends = 3rdparty 17 | app.depends = libs app-static 18 | -------------------------------------------------------------------------------- /src/app/app.pro: -------------------------------------------------------------------------------- 1 | 2 | TEMPLATE = app 3 | 4 | TARGET = Plotline 5 | 6 | QT += core gui webkitwidgets printsupport 7 | 8 | # greaterThan(QT_MAJOR_VERSION, 4): QT += widgets 9 | 10 | INCLUDEPATH += \ 11 | ../lib 12 | 13 | HEADERS += mainwindow.h \ 14 | characteritemmodel.h \ 15 | sceneitemmodel.h \ 16 | preferencesdialog.h \ 17 | plotlineitemmodel.h \ 18 | plotlineitemdelegate.h \ 19 | plotlinedialog.h \ 20 | characterframe.h \ 21 | plotframe.h \ 22 | sceneframe.h \ 23 | chaptersframe.h \ 24 | novelframe.h \ 25 | plotlineappframe.h \ 26 | modelcheckbox.h \ 27 | characterhighlighter.h \ 28 | chaptermodel.h \ 29 | revisiondialog.h \ 30 | markuphighlighter.h \ 31 | styleproxy.h \ 32 | styleproxyparser.h \ 33 | scenelistdialog.h \ 34 | fullscreeneditor.h \ 35 | chapterfilter.h \ 36 | chapterreorderdialog.h \ 37 | publisherdialog.h \ 38 | scenefilter.h \ 39 | savethread.h 40 | 41 | SOURCES += main.cpp \ 42 | mainwindow.cpp \ 43 | characteritemmodel.cpp \ 44 | sceneitemmodel.cpp \ 45 | preferencesdialog.cpp \ 46 | plotlineitemmodel.cpp \ 47 | plotlineitemdelegate.cpp \ 48 | plotlinedialog.cpp \ 49 | characterframe.cpp \ 50 | plotframe.cpp \ 51 | sceneframe.cpp \ 52 | chaptersframe.cpp \ 53 | novelframe.cpp \ 54 | plotlineappframe.cpp \ 55 | modelcheckbox.cpp \ 56 | characterhighlighter.cpp \ 57 | chaptermodel.cpp \ 58 | revisiondialog.cpp \ 59 | markuphighlighter.cpp \ 60 | styleproxy.cpp \ 61 | styleproxyparser.cpp \ 62 | scenelistdialog.cpp \ 63 | fullscreeneditor.cpp \ 64 | chapterfilter.cpp \ 65 | chapterreorderdialog.cpp \ 66 | publisherdialog.cpp \ 67 | scenefilter.cpp \ 68 | savethread.cpp 69 | 70 | ## 71 | # Depending on whether we're building the libraries statically or dynamically, 72 | # Either the .so will be found or the .a will be found. 73 | # There is probably a more elegant solution. 74 | ## 75 | 76 | FORMS += mainwindow.ui \ 77 | preferencesdialog.ui \ 78 | plotlinedialog.ui \ 79 | characterframe.ui \ 80 | plotframe.ui \ 81 | sceneframe.ui \ 82 | chaptersframe.ui \ 83 | novelframe.ui \ 84 | revisiondialog.ui \ 85 | scenelistdialog.ui \ 86 | fullscreeneditor.ui \ 87 | chapterreorderdialog.ui \ 88 | publisherdialog.ui 89 | 90 | RESOURCES += ../res/plotline.qrc 91 | 92 | styles.path = $$DESTDIR/styles 93 | styles.files = $$PWD/styles/*.json 94 | 95 | INSTALLS += styles 96 | 97 | CONFIG += c++11 98 | 99 | #REQUIRES += $$PWD/../lib 100 | #LIBS += $$PWD/../lib 101 | 102 | win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../lib/release/ -lplotline 103 | else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../lib/debug/ -lplotline 104 | else:unix: LIBS += -L$$OUT_PWD/../lib/ -lplotline 105 | 106 | INCLUDEPATH += $$PWD/../lib 107 | DEPENDPATH += $$PWD/../lib 108 | -------------------------------------------------------------------------------- /src/app/chapterfilter.cpp: -------------------------------------------------------------------------------- 1 | #include "chapterfilter.h" 2 | 3 | ChapterFilter::ChapterFilter(Plotline *plotline, QObject *parent) : QSortFilterProxyModel(parent) 4 | { 5 | mPlotline = plotline; 6 | } 7 | 8 | bool ChapterFilter::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const 9 | { 10 | if (0 == mPlotline) 11 | return true; 12 | ChapterModel *model = (ChapterModel *) sourceModel(); 13 | QJsonArray sceneIds = model->data(model->index(source_row, 0), 14 | ChapterModel::SceneRole).toJsonArray(); 15 | 16 | Scene *scene = 0; 17 | bool found = false; 18 | for (QJsonValue v : sceneIds){ 19 | scene = mPlotline->novel()->scene(v.toVariant().toUuid()); 20 | if (scene == 0){ 21 | qWarning() << "chapter filter: Could not find scene with id" 22 | << v.toInt(); 23 | } 24 | if (scene->plotline() == mPlotline) 25 | found = true; 26 | } 27 | return found; 28 | } 29 | 30 | //bool ChapterFilter::filterAcceptsColumn(int source_column, const QModelIndex &source_parent) const 31 | //{ 32 | // return true; //we're row-dependent, not column-dependent 33 | //} 34 | 35 | Plotline *ChapterFilter::plotline() const 36 | { 37 | return mPlotline; 38 | } 39 | 40 | void ChapterFilter::setPlotline(Plotline *plotline) 41 | { 42 | mPlotline = plotline; 43 | emit this->filterChanged(); 44 | } 45 | -------------------------------------------------------------------------------- /src/app/chapterfilter.h: -------------------------------------------------------------------------------- 1 | #ifndef CHAPTERFILTER_H 2 | #define CHAPTERFILTER_H 3 | 4 | #include "plotline.h" 5 | #include "chaptermodel.h" 6 | 7 | class ChapterFilter : public QSortFilterProxyModel 8 | { 9 | Q_OBJECT 10 | public: 11 | explicit ChapterFilter(Plotline *plotline = 0, QObject *parent = 0); 12 | 13 | Plotline *plotline() const; 14 | void setPlotline(Plotline *plotline); 15 | 16 | // bool insertRows(int row, int count, const QModelIndex &parent); 17 | // bool removeRows(int row, int count, const QModelIndex &parent); 18 | 19 | protected: 20 | bool filterAcceptsRow(int source_row, const QModelIndex & source_parent) const; 21 | // bool filterAcceptsColumn(int source_column, const QModelIndex & source_parent) const; 22 | 23 | private: 24 | Plotline *mPlotline; 25 | 26 | signals: 27 | 28 | public slots: 29 | }; 30 | 31 | #endif // CHAPTERFILTER_H 32 | -------------------------------------------------------------------------------- /src/app/chaptermodel.h: -------------------------------------------------------------------------------- 1 | #ifndef CHAPTERMODEL_H 2 | #define CHAPTERMODEL_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "chapter.h" 13 | 14 | class ChapterModel : public QAbstractTableModel 15 | { 16 | Q_OBJECT 17 | public: 18 | explicit ChapterModel(Novel *novel, QObject *parent = 0); 19 | 20 | 21 | // Overrides 22 | int rowCount(const QModelIndex &parent = QModelIndex()) const; 23 | int columnCount(const QModelIndex & parent = QModelIndex()) const; 24 | QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; 25 | QVariant headerData(int section, Qt::Orientation orientation, 26 | int role = Qt::DisplayRole) const; 27 | bool setData(const QModelIndex &index, const QVariant &value, 28 | int role = Qt::DisplayRole); 29 | 30 | bool insertRows(int row, int count, 31 | const QModelIndex & parent = QModelIndex()); 32 | bool removeRows(int row, int count, 33 | const QModelIndex & parent = QModelIndex()); 34 | bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count, 35 | const QModelIndex &destinationParent, int destinationChild); 36 | 37 | 38 | Qt::ItemFlags flags(const QModelIndex & index) const; 39 | Qt::DropActions supportedDropActions() const; 40 | 41 | // QMimeData *dropMimeData(const QMimeData *data, Qt::DropAction action, 42 | // int row, int column, const QModelIndex &parent); 43 | 44 | Novel *novel() const; 45 | void setNovel(Novel *novel); 46 | 47 | // Custom roles 48 | static const int IdRole = Qt::UserRole, 49 | NumberRole = Qt::UserRole + 1, 50 | TitleRole = Qt::UserRole + 2, 51 | ContentRole = Qt::UserRole + 3, 52 | RevisionRole = Qt::UserRole + 4, 53 | RevisionMarkableRole = Qt::UserRole + 5, 54 | CompleteRole = Qt::UserRole + 6, 55 | SceneRole = Qt::UserRole + 7, 56 | AddSceneRole = Qt::UserRole + 8, 57 | RemoveSceneRole = Qt::UserRole + 9; 58 | // Column numbers 59 | static const int NUMBER = 0, 60 | TITLE = 1; 61 | 62 | private: 63 | Novel *mNovel = 0; 64 | int mRowCount, mColCount; 65 | 66 | signals: 67 | 68 | public slots: 69 | // void rowsMoved(const QModelIndex & parent, int start, 70 | // int end, const QModelIndex & destination, 71 | // int row); 72 | }; 73 | 74 | #endif // CHAPTERMODEL_H 75 | -------------------------------------------------------------------------------- /src/app/chapterreorderdialog.cpp: -------------------------------------------------------------------------------- 1 | #include "chapterreorderdialog.h" 2 | #include "ui_chapterreorderdialog.h" 3 | 4 | ChapterReorderDialog::ChapterReorderDialog(QTableView *tableView, 5 | QWidget *parent) : 6 | QDialog(parent), 7 | ui(new Ui::ChapterReorderDialog) 8 | { 9 | ui->setupUi(this); 10 | 11 | mTableView = tableView; 12 | 13 | ChapterModel *model = (ChapterModel *) tableView->model(); 14 | QModelIndex index, selected = tableView->currentIndex(); 15 | QString label, number, title; 16 | 17 | for (int row = 0; row < model->rowCount(); ++row){ 18 | index = model->index(row, 0); 19 | if (selected != index){ 20 | number = model->data(index, ChapterModel::NumberRole).toString(); 21 | title = model->data(index, ChapterModel::TitleRole).toString(); 22 | label = QString("Chapter ") + number; 23 | 24 | if (!title.isNull()) 25 | label += QString(": ") + title; 26 | ui->chapterList->addItem(label, QVariant(row)); 27 | } 28 | } 29 | } 30 | 31 | ChapterReorderDialog::~ChapterReorderDialog() 32 | { 33 | delete ui; 34 | } 35 | 36 | void ChapterReorderDialog::on_ChapterReorderDialog_accepted() 37 | { 38 | ChapterModel *model = (ChapterModel *) mTableView->model(); 39 | QModelIndex selected = mTableView->currentIndex(); 40 | 41 | model->moveRows(QModelIndex(), selected.row(), 1, 42 | QModelIndex(), 43 | ui->chapterList->currentData().toInt()); 44 | } 45 | -------------------------------------------------------------------------------- /src/app/chapterreorderdialog.h: -------------------------------------------------------------------------------- 1 | #ifndef CHAPTERREORDERDIALOG_H 2 | #define CHAPTERREORDERDIALOG_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "chapter.h" 9 | #include "chaptermodel.h" 10 | 11 | namespace Ui { 12 | class ChapterReorderDialog; 13 | } 14 | 15 | class ChapterReorderDialog : public QDialog 16 | { 17 | Q_OBJECT 18 | 19 | public: 20 | explicit ChapterReorderDialog(QTableView *chapter = 0, QWidget *parent = 0); 21 | ~ChapterReorderDialog(); 22 | 23 | private slots: 24 | void on_ChapterReorderDialog_accepted(); 25 | 26 | private: 27 | Ui::ChapterReorderDialog *ui; 28 | 29 | QTableView *mTableView; 30 | }; 31 | 32 | #endif // CHAPTERREORDERDIALOG_H 33 | -------------------------------------------------------------------------------- /src/app/chapterreorderdialog.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | ChapterReorderDialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 400 10 | 103 11 | 12 | 13 | 14 | 15 | 0 16 | 0 17 | 18 | 19 | 20 | 21 | 16777215 22 | 286 23 | 24 | 25 | 26 | Dialog 27 | 28 | 29 | 30 | 31 | 32 | QLayout::SetMaximumSize 33 | 34 | 35 | 36 | 37 | Place before chapter: 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | Qt::Horizontal 48 | 49 | 50 | QDialogButtonBox::Cancel|QDialogButtonBox::Ok 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | buttonBox 62 | accepted() 63 | ChapterReorderDialog 64 | accept() 65 | 66 | 67 | 248 68 | 254 69 | 70 | 71 | 157 72 | 274 73 | 74 | 75 | 76 | 77 | buttonBox 78 | rejected() 79 | ChapterReorderDialog 80 | reject() 81 | 82 | 83 | 316 84 | 260 85 | 86 | 87 | 286 88 | 274 89 | 90 | 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /src/app/chaptersframe.h: -------------------------------------------------------------------------------- 1 | #ifndef CHAPTERSFRAME_H 2 | #define CHAPTERSFRAME_H 3 | 4 | #include 5 | #include 6 | #include "plotlineappframe.h" 7 | #include "chaptermodel.h" 8 | #include "markuphighlighter.h" 9 | #include "scenelistdialog.h" 10 | #include "fullscreeneditor.h" 11 | #include "chapterfilter.h" 12 | #include "chapterreorderdialog.h" 13 | 14 | class PlotlineAppFrame; 15 | 16 | namespace Ui { 17 | class ChaptersFrame; 18 | } 19 | 20 | class ChaptersFrame : public PlotlineAppFrame 21 | { 22 | Q_OBJECT 23 | 24 | public: 25 | explicit ChaptersFrame(MainWindow *mainWindow, QWidget *parent = 0); 26 | ~ChaptersFrame(); 27 | 28 | private: 29 | Ui::ChaptersFrame *ui; 30 | 31 | Chapter *mSelectedChapter; 32 | ChapterModel *mModel; 33 | ChapterFilter *mFilter; 34 | 35 | bool mHasDistractions = true; 36 | 37 | QWidgetList mDistractions; 38 | QTimer *mDistractionTimer; 39 | MarkupHighlighter *mHighlighter; 40 | 41 | static const int PlotlineRole = Qt::UserRole+1; 42 | 43 | void connectSlots(); 44 | void disconnectSlots(); 45 | void blockEditableSignals(); 46 | void unblockEditableSignals(); 47 | 48 | signals: 49 | void chapterSelected(); 50 | void revisionChanged(); 51 | void revisionSet(); 52 | void chapterModified(); 53 | 54 | void hideDistractions(); 55 | void showDistractions(); 56 | 57 | public slots: 58 | void onNovelLoad(); 59 | void onNovelNew(); 60 | 61 | void onChapterModified(); 62 | void onChapterSelected(); 63 | void onRevisionChanged(); 64 | void onRevisionSet(); 65 | void onHideDistractions(); 66 | void onShowDistractions(); 67 | 68 | protected: 69 | void mouseMoveEvent(QMouseEvent *event); 70 | 71 | private slots: 72 | void on_chapterFilter_activated(int index); 73 | void on_addChapter_clicked(); 74 | void on_archiveChapter_clicked(); 75 | void on_deleteChapter_clicked(); 76 | void on_assignScenes_clicked(); 77 | void on_chapterTitle_textEdited(const QString &arg1); 78 | void on_chapterTable_activated(const QModelIndex &index); 79 | void on_chapterTable_clicked(const QModelIndex &index); 80 | void on_chapterComplete_toggled(bool checked); 81 | void on_chapterRevision_valueChanged(int arg1); 82 | // void on_chapterContent_textChanged(); 83 | void on_chapterDistractionFree_clicked(); 84 | void onFullscreenEditorDestroyed(QObject *object); 85 | 86 | void clearLayout(bool enabled, bool clear); 87 | void onDistractionTimeout(void); 88 | void onChapterContentModified(int from, int charsAdded, int charsRemoved); 89 | void on_reorderChapter_clicked(); 90 | }; 91 | 92 | #endif // CHAPTERSFRAME_H 93 | -------------------------------------------------------------------------------- /src/app/characterframe.h: -------------------------------------------------------------------------------- 1 | #ifndef CHARACTERFRAME_H 2 | #define CHARACTERFRAME_H 3 | 4 | #include 5 | #include "plotlineappframe.h" 6 | #include "characteritemmodel.h" 7 | 8 | namespace Ui { 9 | class CharacterFrame; 10 | } 11 | 12 | class CharacterFrame : public PlotlineAppFrame 13 | { 14 | Q_OBJECT 15 | 16 | public: 17 | explicit CharacterFrame(MainWindow *mainWindow, QWidget *parent = 0); 18 | ~CharacterFrame(); 19 | 20 | signals: 21 | void characterModified(); 22 | void characterListModified(); 23 | 24 | public slots: 25 | void onNovelLoad(); 26 | void onNovelNew(); 27 | 28 | void on_addCharacter_clicked(); 29 | 30 | void onCharacterModified(); 31 | void onCharacterListModified(); 32 | 33 | void on_characterList_activated(const QModelIndex &index); 34 | 35 | void on_characterList_clicked(const QModelIndex &index); 36 | 37 | void on_characterName_textChanged(const QString &arg1); 38 | 39 | private slots: 40 | 41 | void on_characterColor_clicked(); 42 | void on_chooseHeadshot_clicked(); 43 | void on_characterLabel_textChanged(const QString &arg1); 44 | 45 | void on_archiveCharacter_clicked(); 46 | 47 | void on_deleteCharacter_clicked(); 48 | 49 | private: 50 | Ui::CharacterFrame *ui; 51 | 52 | CharacterModel *mModel; 53 | Character *mSelectedCharacter; 54 | 55 | // methods 56 | void setCharacterHeadshot(); 57 | void clearCharacterHeadshot(); 58 | 59 | void setButtonColor(const QColor &color); 60 | void clearButtonColor(); 61 | }; 62 | 63 | #endif // CHARACTERFRAME_H 64 | -------------------------------------------------------------------------------- /src/app/characterhighlighter.cpp: -------------------------------------------------------------------------------- 1 | #include "characterhighlighter.h" 2 | #include 3 | 4 | CharacterHighlighter::CharacterHighlighter(Novel *novel, QTextDocument *parent) 5 | : QSyntaxHighlighter(parent) 6 | { 7 | mNovel = novel; 8 | } 9 | 10 | CharacterHighlighter::CharacterHighlighter(Novel *novel, QObject *parent) 11 | : QSyntaxHighlighter(parent) 12 | { 13 | mNovel = novel; 14 | } 15 | 16 | void CharacterHighlighter::highlightBlock(const QString &text) 17 | { 18 | 19 | ParsedCharacterSet set = ParsedCharacterSet::parse(mNovel, text); 20 | 21 | for (int i : set.keys()){ 22 | QTextCharFormat format; 23 | QColor bgColor = set.value(i)->color(); 24 | QColor textColor = QColor(0, 0, 0); // Black text. 25 | if ((255 / 2) > bgColor.value()) 26 | textColor = QColor(255, 255, 255); // White text. 27 | format.setFontWeight(QFont::Bold); 28 | format.setBackground(QBrush(bgColor)); 29 | format.setForeground(QBrush(textColor)); 30 | 31 | qDebug() << "Formating @" << i << ":" << text; 32 | 33 | setFormat(i, set.value(i)->label().count()+1, format); 34 | } 35 | } 36 | 37 | Novel *CharacterHighlighter::novel() const 38 | { 39 | return mNovel; 40 | } 41 | 42 | void CharacterHighlighter::setNovel(Novel *novel) 43 | { 44 | mNovel = novel; 45 | } 46 | 47 | -------------------------------------------------------------------------------- /src/app/characterhighlighter.h: -------------------------------------------------------------------------------- 1 | #ifndef CHARACTERHIGHLIGHTER_H 2 | #define CHARACTERHIGHLIGHTER_H 3 | 4 | #include 5 | #include 6 | 7 | #include "novel.h" 8 | #include "characterparser.h" 9 | 10 | class CharacterHighlighter : public QSyntaxHighlighter 11 | { 12 | Q_OBJECT 13 | public: 14 | CharacterHighlighter(Novel *novel, QTextDocument *parent = 0); 15 | CharacterHighlighter(Novel *novel, QObject *parent = 0); 16 | 17 | Novel *novel() const; 18 | void setNovel(Novel *novel); 19 | 20 | protected: 21 | void highlightBlock(const QString &text) Q_DECL_OVERRIDE; 22 | 23 | signals: 24 | 25 | public slots: 26 | 27 | private: 28 | Novel *mNovel; 29 | }; 30 | 31 | #endif // CHARACTERHIGHLIGHTER_H 32 | -------------------------------------------------------------------------------- /src/app/characteritemmodel.h: -------------------------------------------------------------------------------- 1 | #ifndef CHARACTERITEMMODEL_H 2 | #define CHARACTERITEMMODEL_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "scene.h" 9 | 10 | class CharacterModel : public QAbstractListModel 11 | { 12 | public: 13 | CharacterModel(Novel *novel, QObject *parent = 0); 14 | 15 | const static int CharacterIdRole = Qt::UserRole; 16 | 17 | void addCharacter(); 18 | 19 | int rowCount(const QModelIndex & parent = QModelIndex()) const; 20 | bool insertRows(int row, int count, const QModelIndex & parent = QModelIndex()); 21 | bool removeRows(int row, int count, const QModelIndex & parent = QModelIndex()); 22 | bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count, 23 | const QModelIndex &destinationParent, int destinationChild); 24 | 25 | QVariant data(const QModelIndex & index, 26 | int role = Qt::DisplayRole) const; 27 | QVariant headerData(int section, Qt::Orientation orientation, 28 | int role = Qt::DisplayRole) const; 29 | bool setData(const QModelIndex & index, const QVariant & value, 30 | int role = Qt::EditRole); 31 | Qt::ItemFlags flags(const QModelIndex & index) const; 32 | Qt::DropActions supportedDropActions(); 33 | 34 | QModelIndex lastRow() const; 35 | 36 | // roles 37 | static const int NameRole = Qt::UserRole + 1, 38 | NicknameRole = Qt::UserRole + 2, 39 | LabelRole = Qt::UserRole + 4, 40 | HeadshotRole = Qt::UserRole + 8, 41 | ColorRole = Qt::UserRole + 16, 42 | ArchivedRole = Qt::UserRole + 32; 43 | 44 | private: 45 | 46 | signals: 47 | CharacterModel *mParent; 48 | Novel *mNovel; 49 | 50 | public slots: 51 | }; 52 | 53 | #endif // CHARACTERITEMMODEL_H 54 | -------------------------------------------------------------------------------- /src/app/fullscreeneditor.h: -------------------------------------------------------------------------------- 1 | #ifndef FULLSCREENEDITOR_H 2 | #define FULLSCREENEDITOR_H 3 | 4 | #include 5 | #include "revision.h" 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "markuphighlighter.h" 11 | #include 12 | #include 13 | 14 | namespace Ui { 15 | class FullScreenEditor; 16 | } 17 | 18 | class FullScreenEditor : public QMainWindow 19 | { 20 | Q_OBJECT 21 | 22 | public: 23 | explicit FullScreenEditor(QWidget *parent = 0); 24 | explicit FullScreenEditor(QTextEdit *mEditor, QWidget *parent=0); 25 | ~FullScreenEditor(); 26 | 27 | bool isFullScreen(); 28 | 29 | public slots: 30 | 31 | signals: 32 | void contentChanged(); 33 | 34 | private slots: 35 | void on_chapterContent_textChanged(); 36 | void onEscapeTriggered(bool triggered); 37 | 38 | void on_chapterContent_cursorPositionChanged(); 39 | 40 | private: 41 | Ui::FullScreenEditor *ui; 42 | 43 | QTextEdit *mEditor; 44 | MarkupHighlighter *mHighlighter; 45 | 46 | static const QString DEFAULT_STYLE_FILE; 47 | }; 48 | 49 | #endif // FULLSCREENEDITOR_H 50 | -------------------------------------------------------------------------------- /src/app/fullscreeneditor.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | FullScreenEditor 4 | 5 | 6 | Qt::WindowModal 7 | 8 | 9 | 10 | 0 11 | 0 12 | 800 13 | 600 14 | 15 | 16 | 17 | MainWindow 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 0 28 | 0 29 | 30 | 31 | 32 | QFrame::NoFrame 33 | 34 | 35 | QFrame::Plain 36 | 37 | 38 | 39 | 40 | 41 | 42 | 0 43 | 0 44 | 45 | 46 | 47 | QFrame::NoFrame 48 | 49 | 50 | QTextEdit::NoWrap 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | QLayout::SetDefaultConstraint 61 | 62 | 63 | 64 | 65 | Qt::Horizontal 66 | 67 | 68 | QSizePolicy::Expanding 69 | 70 | 71 | 72 | 400 73 | 5 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 0 83 | 0 84 | 85 | 86 | 87 | Exit 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | exitFullscreenEditor 102 | clicked() 103 | FullScreenEditor 104 | close() 105 | 106 | 107 | 734 108 | 576 109 | 110 | 111 | 597 112 | 570 113 | 114 | 115 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /src/app/main.cpp: -------------------------------------------------------------------------------- 1 | #include "mainwindow.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int main(int argc, char *argv[]) 9 | { 10 | 11 | static const QString APP_DESCRIPTION = QString("Application to help plan,") 12 | + QString(" organize, and write a novel."), 13 | ARG_NOVEL = "novel", 14 | DESC_NOVEL = "Path to a Plotline novel project.", 15 | 16 | T_NOVEL="novel", 17 | T_CHARACTER="character", 18 | T_PLOTLINE="plotline", 19 | T_SCENE="scene", 20 | T_CHAPTER="chapter"; 21 | 22 | static const QStringList TABS({T_NOVEL, T_CHARACTER, T_PLOTLINE, T_SCENE, 23 | T_CHAPTER}); 24 | 25 | static int POS_NOVEL = 0; 26 | 27 | QApplication a(argc, argv); 28 | 29 | // Set the parser. 30 | QCommandLineParser parser; 31 | parser.setApplicationDescription(APP_DESCRIPTION); 32 | parser.addHelpOption(); 33 | parser.addVersionOption(); 34 | parser.addPositionalArgument(ARG_NOVEL, DESC_NOVEL, "[novel]"); 35 | 36 | QCommandLineOption tab = QCommandLineOption( 37 | {"t", "tab"}, 38 | "Open selected tab (novel, character, plotline, scene, chapter)", 39 | T_NOVEL 40 | ); 41 | parser.addOption(tab); 42 | 43 | parser.process(a); 44 | 45 | // Set app information for settings. 46 | QCoreApplication::setOrganizationName("Renegade Engineer"); 47 | QCoreApplication::setOrganizationDomain("rngr.me"); 48 | QCoreApplication::setApplicationName("Plotline"); 49 | 50 | // Get the positional arguments 51 | 52 | QStringList posArgs = parser.positionalArguments(); 53 | QString novelPath; 54 | if (POS_NOVEL < posArgs.length()) 55 | novelPath = posArgs[POS_NOVEL]; 56 | 57 | QString tabName = parser.value("tab"); 58 | if (tabName.isEmpty()) tabName = parser.value("t"); 59 | if (tabName.isEmpty()) tabName = T_NOVEL; 60 | 61 | // Get the correct tab. 62 | if (0 > TABS.indexOf(tabName)){ 63 | qCritical() << "Invalid tab name:" << tabName; 64 | return 1; 65 | } 66 | 67 | MainWindow w; 68 | w.show(); 69 | 70 | if (!novelPath.isNull()){ 71 | w.openNovel(novelPath); 72 | } 73 | 74 | w.openTab(TABS.indexOf(tabName)); 75 | return a.exec(); 76 | } 77 | -------------------------------------------------------------------------------- /src/app/mainwindow.h: -------------------------------------------------------------------------------- 1 | #ifndef MAINWINDOW_H 2 | #define MAINWINDOW_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "savethread.h" 17 | 18 | #include "novel.h" 19 | #include "characteritemmodel.h" 20 | #include "plotlineitemmodel.h" 21 | #include "plotlineitemdelegate.h" 22 | #include "plotlinedialog.h" 23 | #include "preferencesdialog.h" 24 | #include "revisiondialog.h" 25 | 26 | #include "novelframe.h" 27 | #include "characterframe.h" 28 | #include "plotframe.h" 29 | #include "sceneframe.h" 30 | #include "chaptersframe.h" 31 | 32 | class PlotlineAppFrame; 33 | class CharacterFrame; 34 | class PlotFrame; 35 | class SceneFrame; 36 | class ChaptersFrame; 37 | class NovelFrame; 38 | 39 | namespace Ui { 40 | class MainWindow; 41 | } 42 | 43 | class MainWindow : public QMainWindow 44 | { 45 | Q_OBJECT 46 | 47 | public: 48 | explicit MainWindow(QWidget *parent = 0); 49 | ~MainWindow(); 50 | 51 | static const int TENSE_PAST = 0, 52 | TENSE_PRESENT = 1, 53 | TENSE_FUTURE = 2, 54 | TENSE_OTHER = 3, 55 | 56 | POV_1_1 = 0, 57 | POV_2_1 = 1, 58 | POV_3_1 = 2, 59 | POV_1_P = 3, 60 | POV_2_P = 4, 61 | POV_3_P = 5, 62 | POV_OTHER = 6; 63 | 64 | Novel *novel() const; 65 | void setNovel(Novel *novel); 66 | void openNovel(const QString &path); 67 | 68 | void openTab(const int index); 69 | 70 | virtual void closeEvent(QCloseEvent *event); 71 | 72 | signals: 73 | void saveNovel(); 74 | void novelChanged(); 75 | void novelLoaded(); 76 | void novelNew(); 77 | 78 | public slots: 79 | 80 | void show(); 81 | 82 | private slots: 83 | 84 | void onVersionWarningChecked(bool checked); 85 | 86 | void onNovelModified(); 87 | void onSaveNovel(); 88 | void onSaveNovel_started(); 89 | void onSaveNovel_finished(); 90 | void onNovelNew(); 91 | void onNovelLoaded(); 92 | void onHideDistractions(); 93 | void onShowDistractions(); 94 | 95 | void on_actionPreferences_triggered(); 96 | void on_actionNovelNew_triggered(); 97 | void on_actionNovelOpen_triggered(); 98 | void on_actionNovelSave_triggered(); 99 | void on_actionNovelRevisions_triggered(); 100 | void on_actionNovelExport_triggered(); 101 | void on_actionNovelBind_triggered(); 102 | void on_actionNovelSaveAs_triggered(); 103 | void on_actionNovelClose_triggered(); 104 | 105 | protected: 106 | 107 | void mouseMoveEvent(QMouseEvent *event); 108 | 109 | private: 110 | 111 | static const QStringList frameTitles; 112 | static const QString ShowWarning; 113 | 114 | void showWarningMessage(); 115 | 116 | // Frames 117 | NovelFrame *mNovelFrame; 118 | CharacterFrame *mCharacterFrame; 119 | PlotFrame *mPlotFrame; 120 | SceneFrame *mSceneFrame; 121 | ChaptersFrame *mChapterFrame; 122 | QList frames; 123 | 124 | // Window & Dialogs 125 | Ui::MainWindow *ui; 126 | PreferencesDialog *mPrefDialog; 127 | 128 | Novel *mNovel; 129 | bool mIsSaved; 130 | QString mOpenedFile; 131 | QWidgetList mDistractions; 132 | 133 | // widgets 134 | QCheckBox *mShowVersionWarning; 135 | 136 | // Threads 137 | SaveThread *mSaveThread; 138 | 139 | void disconnectAll(); 140 | void connectAll(); 141 | 142 | }; 143 | 144 | #endif // MAINWINDOW_H 145 | -------------------------------------------------------------------------------- /src/app/markuphighlighter.h: -------------------------------------------------------------------------------- 1 | #ifndef MARKDOWNHIGHLIGHTER_H 2 | #define MARKDOWNHIGHLIGHTER_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "styleproxy.h" 11 | #include "styleproxyparser.h" 12 | #include "preferencesdialog.h" 13 | 14 | class MarkupHighlighter : public QSyntaxHighlighter 15 | { 16 | Q_OBJECT 17 | public: 18 | 19 | enum MarkupLanguage { 20 | NoLanguage = 0, 21 | MarkDown, 22 | ReStructuredText 23 | }; 24 | 25 | enum MarkupToken { 26 | Document = 0, // only used for styling/parsing style file. 27 | HtmlEscape, 28 | Header, 29 | BlockQuote, 30 | List, 31 | CodeBlock, 32 | HorizontalRule, 33 | InlineLink, 34 | LinkSource, 35 | LinkTarget, 36 | Emphasis, 37 | Strong, 38 | InlineCode, 39 | Image, 40 | AutomaticLink, 41 | BackslashEscape 42 | }; 43 | 44 | explicit MarkupHighlighter(QObject *parent = 0); 45 | explicit MarkupHighlighter(QTextDocument *parent); 46 | explicit MarkupHighlighter(const MarkupLanguage language, QTextDocument *parent=0); 47 | 48 | virtual void highlightBlock(const QString & text); 49 | 50 | MarkupLanguage getLanguage() const; 51 | void setLanguage(const MarkupLanguage &value); 52 | 53 | private: 54 | 55 | static const QString DEFAULT_STYLE_FILE; 56 | 57 | static const QMap TOKENS; 58 | static const QMap FORMATTING; 59 | MarkupLanguage mLanguage; 60 | 61 | static QMap tokens(); 62 | static QMap formatting(); 63 | QTextCharFormat parseFormat(const QTextCharFormat &baseFormat, 64 | const QJsonObject &obj); 65 | 66 | static MarkupLanguage detectLanguage(const QSettings &settings); 67 | 68 | signals: 69 | 70 | public slots: 71 | }; 72 | 73 | #endif // MARKDOWNHIGHLIGHTER_H 74 | -------------------------------------------------------------------------------- /src/app/modelcheckbox.cpp: -------------------------------------------------------------------------------- 1 | #include "modelcheckbox.h" 2 | 3 | ModelCheckbox::ModelCheckbox(const QString &label, const QVariant &value, 4 | QWidget *parent) : QCheckBox(label, parent) 5 | { 6 | mValue = value; 7 | connect(this, SIGNAL(toggled(bool)), 8 | this, SLOT(onToggled(bool))); 9 | } 10 | 11 | QVariant ModelCheckbox::value() const 12 | { 13 | return mValue; 14 | } 15 | 16 | void ModelCheckbox::setValue(const QVariant &value) 17 | { 18 | mValue = value; 19 | } 20 | 21 | void ModelCheckbox::onToggled(bool checked) 22 | { 23 | emit toggled(checked, mValue); 24 | } 25 | 26 | -------------------------------------------------------------------------------- /src/app/modelcheckbox.h: -------------------------------------------------------------------------------- 1 | #ifndef MODELCHECKBOX_H 2 | #define MODELCHECKBOX_H 3 | 4 | #include 5 | #include 6 | 7 | class ModelCheckbox : public QCheckBox 8 | { 9 | Q_OBJECT 10 | public: 11 | explicit ModelCheckbox(const QString &label, const QVariant &value, 12 | QWidget *parent = 0); 13 | 14 | QVariant value() const; 15 | void setValue(const QVariant &value); 16 | 17 | signals: 18 | 19 | void toggled(bool checked, QVariant value); 20 | 21 | public slots: 22 | 23 | private: 24 | QVariant mValue; 25 | 26 | private slots: 27 | void onToggled(bool checked); 28 | }; 29 | 30 | #endif // MODELCHECKBOX_H 31 | -------------------------------------------------------------------------------- /src/app/novelframe.cpp: -------------------------------------------------------------------------------- 1 | #include "novelframe.h" 2 | #include "ui_novelframe.h" 3 | 4 | NovelFrame::NovelFrame(MainWindow *mainWindow, QWidget *parent) : 5 | PlotlineAppFrame(mainWindow, parent), 6 | ui(new Ui::NovelFrame) 7 | { 8 | ui->setupUi(this); 9 | 10 | ui->novelPartsList->setCurrentRow(0); 11 | } 12 | 13 | NovelFrame::~NovelFrame() 14 | { 15 | delete ui; 16 | } 17 | 18 | void NovelFrame::onNovelLoad() 19 | { 20 | Novel *novel = mainWindow()->novel(); 21 | ui->workingTitle->setText(novel->getWorkingTitle()); 22 | ui->genre->setText(novel->getGenre()); 23 | ui->setting->setText(novel->getSetting()); 24 | ui->pointofView->setCurrentIndex((int) novel->getPointOfView()); 25 | ui->tense->setCurrentIndex((int) novel->getTense()); 26 | 27 | // Author page 28 | Author *author = novel->author(); 29 | ui->authorName->setText(author->name()); 30 | ui->authorPenName->setText(author->penName()); 31 | ui->authorEmail->setText(author->email()); 32 | ui->authorPhone->setText(author->phone()); 33 | // ui->authorAddress->document()->setPlainText(author->address()); 34 | ui->authorAddress->blockSignals(true); 35 | ui->authorAddress->setPlainText(author->address()); 36 | ui->authorAddress->blockSignals(false); 37 | } 38 | 39 | void NovelFrame::onNovelNew() 40 | { 41 | ui->workingTitle->clear(); 42 | ui->genre->clear(); 43 | ui->setting->clear(); 44 | ui->pointofView->clear(); 45 | ui->tense->clear(); 46 | 47 | // Author page 48 | ui->authorAddress->clear(); 49 | ui->authorEmail->clear(); 50 | ui->authorName->clear(); 51 | ui->authorPenName->clear(); 52 | ui->authorPhone->clear(); 53 | } 54 | 55 | void NovelFrame::on_workingTitle_textEdited(const QString &arg1) 56 | { 57 | mMainWindow->novel()->setWorkingTitle(arg1); 58 | emit novelModified(); 59 | } 60 | 61 | void NovelFrame::on_genre_textEdited(const QString &arg1) 62 | { 63 | mMainWindow->novel()->setGenre(arg1); 64 | emit novelModified(); 65 | } 66 | 67 | void NovelFrame::on_setting_textEdited(const QString &arg1) 68 | { 69 | mMainWindow->novel()->setSetting(arg1); 70 | emit novelModified(); 71 | } 72 | 73 | void NovelFrame::on_pointofView_activated(int index) 74 | { 75 | mMainWindow->novel()->setPointOfView((Novel::PointOfView) index); 76 | emit novelModified(); 77 | } 78 | 79 | void NovelFrame::on_tense_activated(int index) 80 | { 81 | mMainWindow->novel()->setTense((Novel::Tense) index); 82 | emit novelModified(); 83 | } 84 | 85 | void NovelFrame::on_authorName_textEdited(const QString &arg1) 86 | { 87 | mainWindow()->novel()->author()->setName(arg1); 88 | emit novelModified(); 89 | } 90 | 91 | void NovelFrame::on_authorPenName_textEdited(const QString &arg1) 92 | { 93 | mainWindow()->novel()->author()->setPenName(arg1); 94 | emit novelModified(); 95 | } 96 | 97 | void NovelFrame::on_authorPhone_textEdited(const QString &arg1) 98 | { 99 | mainWindow()->novel()->author()->setPhone(arg1); 100 | emit novelModified(); 101 | } 102 | 103 | void NovelFrame::on_authorEmail_textEdited(const QString &arg1) 104 | { 105 | mainWindow()->novel()->author()->setEmail(arg1); 106 | emit novelModified(); 107 | } 108 | 109 | void NovelFrame::on_authorAddress_textChanged() 110 | { 111 | mainWindow()->novel()->author()->setAddress(ui->authorAddress->toPlainText()); 112 | emit novelModified(); 113 | } 114 | -------------------------------------------------------------------------------- /src/app/novelframe.h: -------------------------------------------------------------------------------- 1 | #ifndef NOVELFRAME_H 2 | #define NOVELFRAME_H 3 | 4 | #include "plotlineappframe.h" 5 | 6 | namespace Ui { 7 | class NovelFrame; 8 | } 9 | 10 | //class PlotlineAppFrame; 11 | 12 | class NovelFrame : public PlotlineAppFrame 13 | { 14 | Q_OBJECT 15 | 16 | public: 17 | explicit NovelFrame(MainWindow *novel, QWidget *parent = 0); 18 | ~NovelFrame(); 19 | 20 | private slots: 21 | void on_tense_activated(int index); 22 | void on_pointofView_activated(int index); 23 | void on_workingTitle_textEdited(const QString &arg1); 24 | void on_genre_textEdited(const QString &arg1); 25 | void on_setting_textEdited(const QString &arg1); 26 | void on_authorName_textEdited(const QString &arg1); 27 | 28 | void on_authorPenName_textEdited(const QString &arg1); 29 | 30 | void on_authorPhone_textEdited(const QString &arg1); 31 | 32 | void on_authorEmail_textEdited(const QString &arg1); 33 | 34 | void on_authorAddress_textChanged(); 35 | 36 | public slots: 37 | void onNovelLoad(); 38 | void onNovelNew(); 39 | 40 | private: 41 | Ui::NovelFrame *ui; 42 | }; 43 | 44 | #endif // NOVELFRAME_H 45 | -------------------------------------------------------------------------------- /src/app/plotframe.cpp: -------------------------------------------------------------------------------- 1 | #include "plotframe.h" 2 | #include "ui_plotframe.h" 3 | 4 | PlotFrame::PlotFrame(MainWindow *mainWindow, QWidget *parent) : 5 | PlotlineAppFrame(mainWindow, parent), 6 | ui(new Ui::PlotFrame) 7 | { 8 | ui->setupUi(this); 9 | 10 | // Models 11 | mModel = new PlotlineItemModel(mMainWindow->novel()); 12 | ui->plotlineTable->setModel(mModel); 13 | 14 | // Signals 15 | connect(ui->plotlineTable, SIGNAL(doubleClicked(QModelIndex)), 16 | this, SLOT(onPlotlineDoubleClicked(QModelIndex))); 17 | connect(this, SIGNAL(plotlineListModified()), 18 | this, SLOT(onPlotlineListModified())); 19 | 20 | // Properties for table headers. 21 | ui->plotlineTable->setColumnWidth(PlotlineItemModel::BRIEF, 22 | PlotlineItemModel::BRIEF_WIDTH); 23 | ui->plotlineTable->setColumnWidth(PlotlineItemModel::SYNOPSIS, 24 | PlotlineItemModel::SYNOPSIS_WIDTH); 25 | ui->plotlineTable->horizontalHeader()->setStretchLastSection(true); 26 | ui->plotlineTable->setSelectionBehavior(QAbstractItemView::SelectRows); 27 | ui->plotlineTable->setSelectionMode(QAbstractItemView::SingleSelection); 28 | } 29 | 30 | PlotFrame::~PlotFrame() 31 | { 32 | delete ui; 33 | } 34 | 35 | void PlotFrame::onNovelLoad() 36 | { 37 | mModel = new PlotlineItemModel(mainWindow()->novel()); 38 | ui->plotlineTable->setModel(mModel); 39 | } 40 | 41 | void PlotFrame::onNovelNew() 42 | { 43 | mModel = new PlotlineItemModel(mainWindow()->novel()); 44 | } 45 | 46 | 47 | 48 | void PlotFrame::onPlotlineDoubleClicked(QModelIndex index) 49 | { 50 | Q_UNUSED(index); 51 | showPlotlineDialog(); 52 | } 53 | 54 | void PlotFrame::onPlotlineListModified() 55 | { 56 | mModel->removeRows(0, mModel->rowCount()); 57 | // fillPlotlineList(); 58 | } 59 | 60 | void PlotFrame::on_addPlotline_clicked() 61 | { 62 | showPlotlineDialog(true); 63 | } 64 | 65 | void PlotFrame::on_editPlotline_clicked() 66 | { 67 | showPlotlineDialog(); 68 | } 69 | 70 | void PlotFrame::on_archivePlotline_clicked() 71 | { 72 | } 73 | 74 | void PlotFrame::on_deletePlotline_clicked() 75 | { 76 | QString expl = tr("If you delete, it's lost forever. However, archiving") 77 | + tr(" will make it retreivable."); 78 | QMessageBox *messageBox = new QMessageBox(tr("Delete this plotline?"), 79 | expl, QMessageBox::Warning, 80 | QMessageBox::Ok, 81 | QMessageBox::Save, 82 | QMessageBox::Cancel); 83 | messageBox->setButtonText(QMessageBox::Ok, tr("Delete")); 84 | messageBox->setButtonText(QMessageBox::Save, tr("Archive")); 85 | 86 | int res = messageBox->exec(); 87 | if (res == QMessageBox::Ok){ 88 | QModelIndex current = ui->plotlineTable->currentIndex(); 89 | mModel->removeRows(current.row(), 1); 90 | } else if (res == QMessageBox::Save){ 91 | // TODO: archiving. 92 | } 93 | 94 | if (mModel->rowCount() == 0){ 95 | ui->deletePlotline->setEnabled(false); 96 | ui->archivePlotline->setEnabled(false); 97 | ui->editPlotline->setEnabled(false); 98 | } 99 | } 100 | 101 | void PlotFrame::on_searchPlotlines_textChanged(const QString &arg1) 102 | { 103 | Q_UNUSED(arg1); 104 | } 105 | 106 | void PlotFrame::on_filterPlotlines_activated(int index) 107 | { 108 | Q_UNUSED(index); 109 | } 110 | 111 | PlotlineItemModel *PlotFrame::model() const 112 | { 113 | return mModel; 114 | } 115 | 116 | void PlotFrame::setModel(PlotlineItemModel *model) 117 | { 118 | mModel = model; 119 | } 120 | 121 | void PlotFrame::on_plotlineTable_activated(const QModelIndex &index) 122 | { 123 | bool enable = index.isValid(); 124 | ui->deletePlotline->setEnabled(enable); 125 | ui->archivePlotline->setEnabled(enable); 126 | ui->editPlotline->setEnabled(enable); 127 | } 128 | 129 | void PlotFrame::onPlotlineAdded(const QModelIndex &index) 130 | { 131 | ui->plotlineTable->setCurrentIndex(index); 132 | } 133 | 134 | void PlotFrame::showPlotlineDialog(bool isNew) 135 | { 136 | PlotlineDialog *dialog = new PlotlineDialog(ui->plotlineTable, 137 | mainWindow()->novel(), 138 | isNew); 139 | connect(dialog, SIGNAL(plotlineAdded(QModelIndex)), 140 | this, SLOT(onPlotlineAdded(QModelIndex))); 141 | dialog->exec(); 142 | } 143 | 144 | void PlotFrame::on_plotlineTable_clicked(const QModelIndex &index) 145 | { 146 | on_plotlineTable_activated(index); 147 | } 148 | -------------------------------------------------------------------------------- /src/app/plotframe.h: -------------------------------------------------------------------------------- 1 | #ifndef PLOTFRAME_H 2 | #define PLOTFRAME_H 3 | 4 | #include "plotlineappframe.h" 5 | #include "plotlineitemmodel.h" 6 | 7 | class PlotlineAppFrame; 8 | 9 | namespace Ui { 10 | class PlotFrame; 11 | } 12 | 13 | class PlotFrame : public PlotlineAppFrame 14 | { 15 | Q_OBJECT 16 | 17 | public: 18 | explicit PlotFrame(MainWindow *mainWindow, QWidget *parent = 0); 19 | ~PlotFrame(); 20 | 21 | PlotlineItemModel *model() const; 22 | void setModel(PlotlineItemModel *model); 23 | 24 | signals: 25 | void plotlineListModified(); 26 | 27 | public slots: 28 | void onNovelLoad(); 29 | void onNovelNew(); 30 | 31 | void onPlotlineDoubleClicked(QModelIndex index); 32 | void onPlotlineListModified(); 33 | 34 | private slots: 35 | void on_addPlotline_clicked(); 36 | void on_editPlotline_clicked(); 37 | void on_archivePlotline_clicked(); 38 | void on_deletePlotline_clicked(); 39 | void on_searchPlotlines_textChanged(const QString &arg1); 40 | void on_filterPlotlines_activated(int index); 41 | 42 | void on_plotlineTable_activated(const QModelIndex &index); 43 | 44 | void onPlotlineAdded(const QModelIndex &index); 45 | 46 | void on_plotlineTable_clicked(const QModelIndex &index); 47 | 48 | private: 49 | Ui::PlotFrame *ui; 50 | 51 | PlotlineItemModel *mModel; 52 | Plotline *mSelectedPlotline; 53 | void showPlotlineDialog(bool isNew=false); 54 | }; 55 | 56 | #endif // PLOTFRAME_H 57 | -------------------------------------------------------------------------------- /src/app/plotlineappframe.cpp: -------------------------------------------------------------------------------- 1 | #include "plotlineappframe.h" 2 | 3 | #include "novel.h" 4 | 5 | PlotlineAppFrame::PlotlineAppFrame(MainWindow *mainWindow, QWidget *parent) : QFrame(parent) 6 | { 7 | mMainWindow = mainWindow; 8 | } 9 | 10 | MainWindow *PlotlineAppFrame::mainWindow() const 11 | { 12 | return mMainWindow; 13 | } 14 | 15 | void PlotlineAppFrame::setMainWindow(MainWindow *mainWindow) 16 | { 17 | mMainWindow = mainWindow; 18 | } 19 | -------------------------------------------------------------------------------- /src/app/plotlineappframe.h: -------------------------------------------------------------------------------- 1 | #ifndef PLOTLINEAPPFRAME_H 2 | #define PLOTLINEAPPFRAME_H 3 | 4 | #include 5 | #include 6 | #include "novel.h" 7 | 8 | class MainWindow; 9 | 10 | class PlotlineAppFrame : public QFrame 11 | { 12 | Q_OBJECT 13 | 14 | public: 15 | PlotlineAppFrame(MainWindow *mainWindow, QWidget *parent = 0); 16 | 17 | MainWindow *mainWindow() const; 18 | void setMainWindow(MainWindow *mainWindow); 19 | 20 | protected: 21 | MainWindow *mMainWindow; 22 | 23 | signals: 24 | void novelModified(); 25 | 26 | public slots: 27 | virtual void onNovelLoad() = 0; 28 | virtual void onNovelNew() = 0; 29 | }; 30 | 31 | #include "mainwindow.h" 32 | 33 | #endif // PLOTLINEAPPFRAME_H 34 | -------------------------------------------------------------------------------- /src/app/plotlinedialog.cpp: -------------------------------------------------------------------------------- 1 | #include "plotlinedialog.h" 2 | #include "ui_plotlinedialog.h" 3 | 4 | PlotlineDialog::PlotlineDialog(QTableView *tableView, 5 | Novel *novel, 6 | bool isNew, 7 | QWidget *parent) : 8 | QDialog(parent), 9 | ui(new Ui::PlotlineDialog) 10 | { 11 | ui->setupUi(this); 12 | 13 | mTableView = tableView; 14 | mNovel = novel; 15 | 16 | mCharacterList = QMap(); 17 | mIsNew = isNew; 18 | 19 | PlotlineItemModel *model = (PlotlineItemModel *) mTableView->model(); 20 | QModelIndex index = mTableView->currentIndex(); 21 | 22 | QList selectedCharacters; 23 | if (index.isValid() && !mIsNew){ 24 | mIsNew = false; 25 | QString brief = model->data(index, PlotlineItemModel::BriefRole) 26 | .toString(), 27 | synopsis = model->data(index, PlotlineItemModel::SynopsisRole) 28 | .toString(); 29 | QColor color = model->data(index, PlotlineItemModel::ColorRole) 30 | .value(); 31 | QJsonArray jCharacters = model->data(index, 32 | PlotlineItemModel::CharacterRole) 33 | .toJsonArray(); 34 | ui->plotlineBrief->setText(brief); 35 | ui->plotlineSynopsis->setText(synopsis); 36 | onColorSelected(QColor(color)); 37 | 38 | for (QJsonValue v : jCharacters) 39 | selectedCharacters << mNovel->character(QUuid(v.toString())); 40 | } else { 41 | mIsNew = true; 42 | setWindowTitle(tr("New Plotline")); 43 | } 44 | 45 | for (Character *c : mNovel->characters()) { 46 | QCheckBox *checkbox = new QCheckBox(c->name()); 47 | bool checked = selectedCharacters.contains(c); 48 | checkbox->setChecked(checked); 49 | mCharacterList.insert(checkbox, c); 50 | ui->characterListLayout->addWidget(checkbox); 51 | } 52 | } 53 | 54 | PlotlineDialog::~PlotlineDialog() 55 | { 56 | delete ui; 57 | // The plotline will be deleted with the novel. 58 | } 59 | 60 | void PlotlineDialog::on_plotlineColor_clicked() 61 | { 62 | QColorDialog *dialog = new QColorDialog(this); 63 | connect(dialog, SIGNAL(colorSelected(QColor)), 64 | this, SLOT(onColorSelected(QColor))); 65 | dialog->exec(); 66 | } 67 | 68 | void PlotlineDialog::onColorSelected(const QColor &color) 69 | { 70 | mColor = color; 71 | if (color.isValid()){ 72 | QImage image = QImage(40, 30, QImage::Format_RGB32); 73 | image.fill(color); 74 | QIcon icon = QIcon(QPixmap::fromImage(image)); 75 | ui->plotlineColor->setIcon(icon); 76 | } else { 77 | } 78 | } 79 | 80 | 81 | void PlotlineDialog::on_clearPlotlineColor_clicked() 82 | { 83 | onColorSelected(QColor()); 84 | } 85 | 86 | void PlotlineDialog::on_buttonBox_accepted() 87 | { 88 | PlotlineItemModel *model = (PlotlineItemModel *) mTableView->model(); 89 | QModelIndex index = mTableView->currentIndex(); 90 | if (mIsNew){ 91 | if (!model->insertRows(index.row()+1, 1)){ 92 | qWarning("[+] plotline - could not insert rows at [%d, %d]", 93 | index.row(), index.column()); 94 | return; 95 | } 96 | index = model->index(index.row()+1, 0); // go to the inserted row. 97 | } 98 | 99 | QJsonArray charIds; 100 | for (QCheckBox *cb : mCharacterList.keys()) 101 | if (cb->isChecked()) 102 | charIds << QJsonValue(mCharacterList[cb]->id().toString()); 103 | 104 | model->setData(index, ui->plotlineBrief->text(), PlotlineItemModel 105 | ::BriefRole); 106 | model->setData(index, ui->plotlineSynopsis->toPlainText(), 107 | PlotlineItemModel::SynopsisRole); 108 | model->setData(index, charIds, PlotlineItemModel::CharacterRole); 109 | model->setData(index, mColor, PlotlineItemModel::ColorRole); 110 | 111 | emit mTableView->activated(index); 112 | 113 | if (mIsNew) 114 | emit plotlineAdded(index); 115 | else 116 | emit plotlineModified(index); 117 | } 118 | 119 | void PlotlineDialog::on_characterSearch_textEdited(const QString &arg1) 120 | { 121 | for (QCheckBox *cb : mCharacterList.keys()){ 122 | Character *c = mCharacterList[cb]; 123 | if (arg1.isEmpty() || (c->name().toLower().contains(arg1.toLower())) 124 | || (arg1.startsWith("@") && c->label().contains(arg1.mid(1)))){ 125 | ui->characterListLayout->addWidget(cb); 126 | cb->setVisible(true); 127 | } else { 128 | ui->characterListLayout->removeWidget(cb); 129 | cb->setVisible(false); 130 | } 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/app/plotlinedialog.h: -------------------------------------------------------------------------------- 1 | #ifndef PLOTLINEDIALOG_H 2 | #define PLOTLINEDIALOG_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "plotline.h" 15 | #include "plotframe.h" 16 | #include "plotlineitemmodel.h" 17 | 18 | class PlotFrame; 19 | 20 | namespace Ui { 21 | class PlotlineDialog; 22 | } 23 | 24 | class PlotlineDialog : public QDialog 25 | { 26 | Q_OBJECT 27 | 28 | public: 29 | explicit PlotlineDialog(QTableView *tableView, 30 | Novel *novel, 31 | bool isNew = false, 32 | QWidget *parent = 0); 33 | ~PlotlineDialog(); 34 | 35 | private slots: 36 | void on_plotlineColor_clicked(); 37 | void onColorSelected(const QColor &color); 38 | void on_clearPlotlineColor_clicked(); 39 | void on_buttonBox_accepted(); 40 | 41 | void on_characterSearch_textEdited(const QString &arg1); 42 | 43 | signals: 44 | 45 | void plotlineAdded(const QModelIndex &index); 46 | void plotlineModified(const QModelIndex &index); 47 | void canceled(); 48 | 49 | private: 50 | Ui::PlotlineDialog *ui; 51 | 52 | Novel *mNovel; 53 | QTableView *mTableView; 54 | QMap mCharacterList; 55 | 56 | QColor mColor; 57 | 58 | bool mIsNew; 59 | }; 60 | 61 | #endif // PLOTLINEDIALOG_H 62 | -------------------------------------------------------------------------------- /src/app/plotlineitemdelegate.cpp: -------------------------------------------------------------------------------- 1 | #include "plotlineitemdelegate.h" 2 | 3 | PlotlineItemDelegate::PlotlineItemDelegate(QObject *parent) 4 | : QStyledItemDelegate(parent) 5 | { 6 | } 7 | 8 | QWidget *PlotlineItemDelegate::createEditor(QWidget *parent, 9 | const QStyleOptionViewItem &option, 10 | const QModelIndex &index) const 11 | { 12 | Q_UNUSED(option); 13 | QWidget *widget = 0; 14 | switch(index.column()){ 15 | case BRIEF: 16 | case SYNOPSIS: 17 | widget = (QWidget *) new QTextEdit(); 18 | break; 19 | case CHARACTERS: 20 | widget = new QPushButton(parent); 21 | break; 22 | } 23 | 24 | return widget; 25 | } 26 | 27 | void PlotlineItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const 28 | { 29 | if (editor == 0) return; 30 | 31 | QTextEdit *textEdit = 0; 32 | QPushButton *button = 0; 33 | 34 | switch (index.column()){ 35 | case BRIEF: 36 | case SYNOPSIS: 37 | textEdit = static_cast(editor); 38 | textEdit->setText(index.model()->data(index, Qt::EditRole).toString()); 39 | break; 40 | case CHARACTERS: 41 | button = static_cast(editor); 42 | Q_UNUSED(button); 43 | break; 44 | } 45 | } 46 | 47 | void PlotlineItemDelegate::setModelData(QWidget *editor, 48 | QAbstractItemModel *model, 49 | const QModelIndex &index) const 50 | { 51 | QLineEdit *lineEditor = 0; 52 | QPushButton *button = 0; 53 | 54 | switch (index.column()){ 55 | case BRIEF: 56 | case SYNOPSIS: 57 | lineEditor = static_cast(editor); 58 | model->setData(index, lineEditor->text()); 59 | break; 60 | case CHARACTERS: 61 | button = static_cast(editor); 62 | Q_UNUSED(button); 63 | break; 64 | } 65 | } 66 | 67 | void PlotlineItemDelegate::updateEditorGeometry(QWidget *editor, 68 | const QStyleOptionViewItem &option, 69 | const QModelIndex &index) const 70 | { 71 | Q_UNUSED(index); 72 | editor->setGeometry(option.rect); 73 | } 74 | 75 | -------------------------------------------------------------------------------- /src/app/plotlineitemdelegate.h: -------------------------------------------------------------------------------- 1 | #ifndef SCENEITEMDELEGATE_H 2 | #define SCENEITEMDELEGATE_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "novel.h" 13 | #include "plotline.h" 14 | 15 | class Novel; 16 | class Plotline; 17 | 18 | class PlotlineItemDelegate : public QStyledItemDelegate 19 | { 20 | 21 | Q_OBJECT 22 | 23 | public: 24 | PlotlineItemDelegate(QObject *parent = 0); 25 | 26 | QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, 27 | const QModelIndex &index) const Q_DECL_OVERRIDE; 28 | 29 | void setEditorData(QWidget *editor, const QModelIndex &index) const Q_DECL_OVERRIDE; 30 | void setModelData(QWidget *editor, QAbstractItemModel *model, 31 | const QModelIndex &index) const Q_DECL_OVERRIDE; 32 | 33 | void updateEditorGeometry(QWidget *editor, 34 | const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE; 35 | 36 | private: 37 | Novel *mNovel; 38 | 39 | static const int BRIEF = 0, 40 | SYNOPSIS = 1, 41 | CHARACTERS = 2; 42 | 43 | signals: 44 | 45 | public slots: 46 | }; 47 | 48 | #endif // SCENEITEMDELEGATE_H 49 | -------------------------------------------------------------------------------- /src/app/plotlineitemmodel.h: -------------------------------------------------------------------------------- 1 | #ifndef PLOTLINEITEMVIEW_H 2 | #define PLOTLINEITEMVIEW_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "novel.h" 13 | 14 | class Novel; 15 | 16 | class PlotlineItemModel : public QAbstractTableModel 17 | { 18 | Q_OBJECT 19 | public: 20 | PlotlineItemModel(Novel *novel = 0, QObject *parent = 0); 21 | 22 | // Read methods 23 | int rowCount(const QModelIndex & parent = QModelIndex()) const; 24 | int columnCount(const QModelIndex & parent = QModelIndex()) const; 25 | QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const; 26 | bool setData(const QModelIndex &index, const QVariant &value, int role); 27 | QVariant headerData(int section, Qt::Orientation orientation, 28 | int role = Qt::DisplayRole) const; 29 | 30 | bool insertRows(int row, int count, 31 | const QModelIndex & parent = QModelIndex()); 32 | bool removeRows(int row, int count, 33 | const QModelIndex & parent = QModelIndex()); 34 | 35 | Qt::ItemFlags flags(const QModelIndex & index) const; 36 | 37 | // Custom roles 38 | static const int PlotlineId = Qt::UserRole; 39 | 40 | // Column numbers 41 | static const int BRIEF = 0, 42 | SYNOPSIS = 1, 43 | CHARACTERS = 2, 44 | // Column widths 45 | BRIEF_WIDTH = 200, 46 | SYNOPSIS_WIDTH = 420, 47 | CHARACTERS_WIDTH = 100; 48 | 49 | static const int CharacterRole = Qt::UserRole, 50 | BriefRole = Qt::UserRole + 1, 51 | SynopsisRole = Qt::UserRole + 2, 52 | ColorRole = Qt::UserRole + 4; 53 | 54 | Novel *novel() const; 55 | void setNovel(Novel *novel); 56 | 57 | private: 58 | 59 | Novel *mNovel; 60 | int mRowCount; 61 | 62 | signals: 63 | 64 | public slots: 65 | }; 66 | 67 | #endif // PLOTLINEITEMVIEW_H 68 | -------------------------------------------------------------------------------- /src/app/preferencesdialog.h: -------------------------------------------------------------------------------- 1 | #ifndef PREFERENCESDIALOG_H 2 | #define PREFERENCESDIALOG_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "markuphighlighter.h" 9 | 10 | namespace Ui { 11 | class PreferencesDialog; 12 | } 13 | 14 | class PreferencesDialog : public QDialog 15 | { 16 | Q_OBJECT 17 | 18 | public: 19 | explicit PreferencesDialog(QWidget *parent = 0); 20 | ~PreferencesDialog(); 21 | 22 | static const int DEFAULT_FONT_SIZE = 12; 23 | 24 | static const QString DEFAULT_PROJECT_DIRECTORY, 25 | DEFAULT_HEADSHOT_DIRECTORY, 26 | OPEN_LAST_PROJECT, 27 | DISTRACTION_FREE_MODE, 28 | DFM_EDITOR_SIZE, 29 | WIDGETS_TIMEOUT, 30 | SPELL_CHECK, 31 | WORD_WRAP_WIDTH, 32 | MARKUP, 33 | FONT, 34 | FONT_SIZE, 35 | COLOR_SCHEME; 36 | 37 | enum DistractionFreeMode { 38 | DfmDisabled = 0, 39 | ShowWindowed, 40 | ShowFullScreen 41 | }; 42 | 43 | enum SpellCheckMode { 44 | SpellCheckDisabled = 0, 45 | SpellCheckNotInDfm, 46 | SpellCheckAlways, 47 | }; 48 | 49 | private slots: 50 | void on_chooseDefaultProjectDirectory_clicked(); 51 | void on_preferencesButtonBox_accepted(); 52 | // void on_preferencesButtonBox_rejected(); 53 | 54 | void on_widgetTimeout_sliderMoved(int position); 55 | 56 | private: 57 | Ui::PreferencesDialog *ui; 58 | static const QStringList WIDGET_TIMEOUTS; 59 | static const int TIMEOUT_VALUES[6]; 60 | static const float SIZE_PERCENTAGES[4]; 61 | }; 62 | 63 | #endif // PREFERENCESDIALOG_H 64 | -------------------------------------------------------------------------------- /src/app/publisherdialog.cpp: -------------------------------------------------------------------------------- 1 | #include "publisherdialog.h" 2 | #include "ui_publisherdialog.h" 3 | #include 4 | 5 | const QStringList PublisherDialog::Extensions = QStringList({ 6 | "epub", 7 | "mobi", 8 | "markdown", 9 | "markdown", 10 | "pdf", 11 | "html", 12 | "odt" 13 | }), 14 | PublisherDialog::ChapterPlaceholders = QStringList({ 15 | "", 16 | "%n", 17 | "%t", 18 | "%I", 19 | "%1", 20 | "%O" 21 | }), 22 | PublisherDialog::HeaderPlaceholders = QStringList({ 23 | "", 24 | "%t", // title 25 | "%s", // short title 26 | "%l", // author's last name 27 | "%a", // author's full name 28 | "%p", // page number 29 | }); 30 | 31 | PublisherDialog::PublisherDialog(Novel *novel, QWidget *parent) : 32 | QDialog(parent), 33 | ui(new Ui::PublisherDialog) 34 | { 35 | ui->setupUi(this); 36 | mNovel = novel; 37 | } 38 | 39 | PublisherDialog::~PublisherDialog() 40 | { 41 | delete ui; 42 | } 43 | 44 | void PublisherDialog::on_chooseDirectory_clicked() 45 | { 46 | QString outDir = QFileDialog::getExistingDirectory(this, 47 | "Output Directory", 48 | QDir::homePath()); 49 | outDir += QDir::separator() + mNovel->getWorkingTitle() + QChar('.') 50 | + Extensions[ui->format->currentIndex()]; 51 | ui->outputFile->setText(outDir); 52 | } 53 | 54 | void PublisherDialog::on_chapterHeaderCombo_activated(int index) 55 | { 56 | if (index == 0) 57 | return; 58 | QString text = ui->chapterHeaderText->text(); 59 | text = text.insert(index, ChapterPlaceholders[index]); 60 | ui->chapterHeaderText->setText(text); 61 | ui->chapterHeaderCombo->blockSignals(true); 62 | ui->chapterHeaderCombo->setCurrentIndex(0); 63 | ui->chapterHeaderCombo->blockSignals(false); 64 | } 65 | 66 | void PublisherDialog::on_titleCombo_activated(int index) 67 | { 68 | if (index == 0) 69 | return; 70 | QString text = ui->titleText->text(); 71 | text = text.insert(index, HeaderPlaceholders[index]); 72 | ui->titleText->setText(text); 73 | ui->titleText->blockSignals(true); 74 | ui->titleCombo->setCurrentIndex(0); 75 | ui->titleText->blockSignals(false); 76 | } 77 | 78 | void PublisherDialog::updatePreview() 79 | { 80 | // todo 81 | } 82 | 83 | QString PublisherDialog::formatTitleHeader() const 84 | { 85 | QString name = mNovel->author()->name(); 86 | if (!mNovel->author()->penName().isEmpty()) 87 | name = mNovel->author()->penName(); 88 | return QString("# ") + mNovel->getWorkingTitle() + QString("\n") 89 | + QString("# by\n# ") + name + QString("\n\n"); 90 | } 91 | 92 | QString PublisherDialog::formatChapterHeader(Chapter *chapter) const 93 | { 94 | QString format = ui->chapterHeaderText->text(); 95 | return format.replace("%n", "\n# ") 96 | .replace("%t", chapter->title()) 97 | .replace("%I", QString::number(chapter->number())) 98 | .replace("%1", QString::number(chapter->number())) 99 | .replace("%O", QString::number(chapter->number())) 100 | + QString("\n\n"); 101 | } 102 | 103 | void PublisherDialog::on_chapterHeaderText_cursorPositionChanged(int arg1, int arg2) 104 | { 105 | Q_UNUSED(arg1); 106 | chapterTextPos = arg2; 107 | } 108 | 109 | void PublisherDialog::on_titleText_cursorPositionChanged(int arg1, int arg2) 110 | { 111 | Q_UNUSED(arg1); 112 | headerTextPos = arg2; 113 | } 114 | 115 | void PublisherDialog::on_PublisherDialog_accepted() 116 | { 117 | QString text = formatTitleHeader(); 118 | for (Chapter *c : mNovel->chapters()){ 119 | text += formatChapterHeader(c); 120 | text += c->latestContent() + QString("\n\n"); 121 | } 122 | QString outPath = ui->outputFile->text(); 123 | QFile *outFile = new QFile(outPath); 124 | int success = outFile->open(QFile::WriteOnly); 125 | if (!success){ 126 | QMessageBox::critical(this, tr("Error"), 127 | tr("Could not publish to \"") 128 | + outPath + tr("\""), QMessageBox::Ok, 129 | QMessageBox::NoButton); 130 | qWarning() << "Could not write to" << outPath; 131 | return; 132 | } 133 | outFile->write(text.toStdString().data(), text.length()); 134 | qDebug() << "Wrote" << text.length() << "bytes to" << outPath; 135 | outFile->close(); 136 | delete outFile; 137 | } 138 | -------------------------------------------------------------------------------- /src/app/publisherdialog.h: -------------------------------------------------------------------------------- 1 | #ifndef PUBLISHERDIALOG_H 2 | #define PUBLISHERDIALOG_H 3 | 4 | #include 5 | #include "novel.h" 6 | 7 | namespace Ui { 8 | class PublisherDialog; 9 | } 10 | 11 | class PublisherDialog : public QDialog 12 | { 13 | Q_OBJECT 14 | 15 | public: 16 | explicit PublisherDialog(Novel *novel, QWidget *parent = 0); 17 | ~PublisherDialog(); 18 | 19 | private slots: 20 | void on_chooseDirectory_clicked(); 21 | void on_chapterHeaderCombo_activated(int index); 22 | void on_chapterHeaderText_cursorPositionChanged(int arg1, int arg2); 23 | void on_titleText_cursorPositionChanged(int arg1, int arg2); 24 | void on_titleCombo_activated(int index); 25 | 26 | void on_PublisherDialog_accepted(); 27 | 28 | private: 29 | Ui::PublisherDialog *ui; 30 | 31 | Novel *mNovel; 32 | 33 | static const QStringList Extensions, ChapterPlaceholders, 34 | HeaderPlaceholders; 35 | 36 | static const int Blank = 0, 37 | NewLine = 1, // %n 38 | ChapterTitle = 2, // %t 39 | RomanNumber = 3, // %I 40 | LatinNumber = 4, // %1 41 | TextNumber = 5; // %O 42 | 43 | int chapterTextPos = 0, headerTextPos = 0; 44 | 45 | void updatePreview(); 46 | QString formatTitleHeader() const; 47 | QString formatChapterHeader(Chapter *) const; 48 | }; 49 | 50 | #endif // PUBLISHERDIALOG_H 51 | -------------------------------------------------------------------------------- /src/app/revisiondialog.cpp: -------------------------------------------------------------------------------- 1 | #include "revisiondialog.h" 2 | #include "ui_revisiondialog.h" 3 | 4 | RevisionDialog::RevisionDialog(MainWindow *mainWindow, QWidget *parent) : 5 | QDialog(parent), 6 | ui(new Ui::RevisionDialog) 7 | { 8 | ui->setupUi(this); 9 | mMainWindow = mainWindow; 10 | 11 | Novel *novel = mainWindow->novel(); 12 | int count = novel->revisionCount(), 13 | rev = novel->currentRevision(); 14 | mTmpRevisions = novel->revisions(); 15 | 16 | ui->revisionNumber->setRange(1, count); 17 | ui->revisionNumber->setValue(rev+1); 18 | ui->revisionComments->setPlainText(novel->revisionComment(rev)); 19 | 20 | mTempRevision = rev; 21 | } 22 | 23 | RevisionDialog::~RevisionDialog() 24 | { 25 | delete ui; 26 | } 27 | 28 | MainWindow *RevisionDialog::mainWindow() const 29 | { 30 | return mMainWindow; 31 | } 32 | 33 | void RevisionDialog::setMainWindow(MainWindow *mainWindow) 34 | { 35 | mMainWindow = mainWindow; 36 | } 37 | 38 | void RevisionDialog::on_addRevision_clicked() 39 | { 40 | // Novel *novel = mainWindow()->novel(); 41 | mTmpRevisions.append(QString()); 42 | ui->revisionNumber->setMaximum(mTmpRevisions.count()); 43 | ui->revisionNumber->setValue(mTmpRevisions.count()); 44 | } 45 | 46 | void RevisionDialog::on_revisionNumber_valueChanged(int arg1) 47 | { 48 | qDebug() << "revision value changed:" << arg1; 49 | mTempRevision = arg1 - 1; 50 | ui->revisionComments->setPlainText(mTmpRevisions[mTempRevision]); 51 | } 52 | 53 | void RevisionDialog::on_buttonBox_accepted() 54 | { 55 | qDebug() << "Revisions changed:" << "\n" 56 | << "New revisions:" << mTmpRevisions << "\n" 57 | << "Selected revision:" << mTempRevision; 58 | if (mTempRevision != mainWindow()->novel()->currentRevision() || 59 | mTmpRevisions != mainWindow()->novel()->revisions()) 60 | emit mainWindow()->novelChanged(); 61 | mainWindow()->novel()->setRevisions(mTmpRevisions); 62 | mainWindow()->novel()->setCurrentRevision(mTempRevision); 63 | } 64 | 65 | void RevisionDialog::on_revisionComments_textChanged() 66 | { 67 | qDebug() << "revisions[" << mTempRevision << "] =" 68 | << ui->revisionComments->toPlainText(); 69 | mTmpRevisions[mTempRevision] = ui->revisionComments->toPlainText(); 70 | } 71 | -------------------------------------------------------------------------------- /src/app/revisiondialog.h: -------------------------------------------------------------------------------- 1 | #ifndef REVISIONDIALOG_H 2 | #define REVISIONDIALOG_H 3 | 4 | #include 5 | 6 | #include "mainwindow.h" 7 | 8 | namespace Ui { 9 | class RevisionDialog; 10 | } 11 | 12 | class RevisionDialog : public QDialog 13 | { 14 | Q_OBJECT 15 | 16 | public: 17 | explicit RevisionDialog(MainWindow *mainWindow, QWidget *parent = 0); 18 | ~RevisionDialog(); 19 | 20 | MainWindow *mainWindow() const; 21 | void setMainWindow(MainWindow *mainWindow); 22 | 23 | private slots: 24 | void on_addRevision_clicked(); 25 | void on_revisionNumber_valueChanged(int arg1); 26 | void on_buttonBox_accepted(); 27 | void on_revisionComments_textChanged(); 28 | 29 | private: 30 | Ui::RevisionDialog *ui; 31 | 32 | MainWindow *mMainWindow; 33 | 34 | QStringList mTmpRevisions; 35 | int mTempRevision = 0; 36 | }; 37 | 38 | #endif // REVISIONDIALOG_H 39 | -------------------------------------------------------------------------------- /src/app/revisiondialog.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | RevisionDialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 456 10 | 324 11 | 12 | 13 | 14 | Dialog 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | Comments: 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 1 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 0 45 | 0 46 | 47 | 48 | 49 | Add Revision 50 | 51 | 52 | 53 | 54 | 55 | 56 | :/images/images/list-add.png:/images/images/list-add.png 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 0 67 | 0 68 | 69 | 70 | 71 | Go To Revision: 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | Qt::Horizontal 81 | 82 | 83 | QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | buttonBox 97 | accepted() 98 | RevisionDialog 99 | accept() 100 | 101 | 102 | 248 103 | 254 104 | 105 | 106 | 157 107 | 274 108 | 109 | 110 | 111 | 112 | buttonBox 113 | rejected() 114 | RevisionDialog 115 | reject() 116 | 117 | 118 | 316 119 | 260 120 | 121 | 122 | 286 123 | 274 124 | 125 | 126 | 127 | 128 | 129 | -------------------------------------------------------------------------------- /src/app/savethread.cpp: -------------------------------------------------------------------------------- 1 | #include "savethread.h" 2 | 3 | void SaveThread::run() 4 | { 5 | mNovel->writeTo(mOpenedFile); 6 | } 7 | 8 | SaveThread::SaveThread(Novel *novel, const QString &openedFile) 9 | { 10 | mNovel = novel; 11 | mOpenedFile = openedFile; 12 | } 13 | -------------------------------------------------------------------------------- /src/app/savethread.h: -------------------------------------------------------------------------------- 1 | #ifndef SAVETHREAD_H 2 | #define SAVETHREAD_H 3 | 4 | #include 5 | 6 | class MainWindow; 7 | class Novel; 8 | 9 | class SaveThread : public QThread 10 | { 11 | Q_OBJECT 12 | 13 | public: 14 | SaveThread(Novel *novel, const QString &openedFile = QString()); 15 | 16 | protected: 17 | void run(); 18 | 19 | private: 20 | Novel *mNovel; 21 | QString mOpenedFile; 22 | }; 23 | 24 | #include "mainwindow.h" 25 | #include "novel.h" 26 | 27 | #endif // SAVETHREAD_H 28 | -------------------------------------------------------------------------------- /src/app/scenefilter.cpp: -------------------------------------------------------------------------------- 1 | #include "scenefilter.h" 2 | 3 | SceneFilter::SceneFilter(Novel *novel, Plotline *plotline, QObject *parent) 4 | : QSortFilterProxyModel(parent) 5 | { 6 | mNovel = novel; 7 | mPlotline = plotline; 8 | mSearch = QString(); 9 | // connect(this, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector)), 10 | // this, SLOT(onDataChanged(QModelIndex,QModelIndex,QVector))); 11 | // connect(this, SIGNAL(rowsInserted(QModelIndex,int,int)), 12 | // this, SLOT(onRowsInserted(QModelIndex, int, int))); 13 | } 14 | 15 | Qt::ItemFlags SceneFilter::flags(const QModelIndex &index) const 16 | { 17 | Qt::ItemFlags defaultFlags = QSortFilterProxyModel::flags(index); 18 | 19 | if (index.isValid()) 20 | return Qt::ItemIsDragEnabled | defaultFlags; 21 | else 22 | return Qt::ItemIsDropEnabled | defaultFlags; 23 | } 24 | 25 | Qt::DropActions SceneFilter::supportedDropActions() const 26 | { 27 | return Qt::MoveAction; 28 | } 29 | 30 | bool SceneFilter::filterAcceptsRow(int source_row, 31 | const QModelIndex &source_parent) const 32 | { 33 | Q_UNUSED(source_parent); 34 | Scene *scene = mNovel->scenes()[source_row]; 35 | 36 | bool b = ((mPlotline == 0 || (scene->plotline() == mPlotline)) && 37 | (mSearch.isEmpty() || (scene->headline().toLower().contains(mSearch.toLower()) 38 | || scene->action().toLower().contains(mSearch.toLower())) )); 39 | return b; 40 | } 41 | 42 | bool SceneFilter::insertRows(int row, int count, const QModelIndex &parent) 43 | { 44 | beginInsertRows(parent, row, row+(count-1)); 45 | SceneModel *sceneModel = (SceneModel *) sourceModel(); 46 | qDebug() << "[+] scene filter - add " << count << "scenes at" << row; 47 | bool res = sceneModel->insertRows(row, count, mPlotline, parent); 48 | endInsertRows(); 49 | 50 | return res; 51 | } 52 | 53 | bool SceneFilter::removeRows(const QModelIndexList &indexList) 54 | { 55 | QList toDelete; 56 | for (QModelIndex index : indexList) 57 | toDelete << data(index, SceneModel::IdRole).toUuid(); 58 | for (QUuid id : toDelete){ 59 | Scene *scene = mNovel->scene(id); 60 | if (scene) 61 | qDebug() << "[-] Removing scene" << scene->headline(); 62 | else 63 | qDebug() << "[!] Could not get scene by" << id; 64 | mNovel->removeScene(scene); 65 | } 66 | return true; 67 | } 68 | 69 | bool SceneFilter::moveRows(const QModelIndex &sourceParent, int sourceRow, 70 | int count, const QModelIndex &destinationParent, 71 | int destinationChild) 72 | { 73 | beginMoveRows(sourceParent, sourceRow, sourceRow+(count-1), destinationParent, 74 | destinationChild); 75 | 76 | endMoveRows(); 77 | } 78 | 79 | Plotline *SceneFilter::plotline() const 80 | { 81 | return mPlotline; 82 | } 83 | 84 | void SceneFilter::setPlotline(Plotline *plotline) 85 | { 86 | mPlotline = plotline; 87 | emit this->filterChanged(); 88 | } 89 | 90 | QString SceneFilter::search() const 91 | { 92 | return mSearch; 93 | } 94 | 95 | void SceneFilter::setSearch(const QString &search) 96 | { 97 | mSearch = search; 98 | emit this->filterChanged(); 99 | } 100 | 101 | Novel *SceneFilter::novel() const 102 | { 103 | return mNovel; 104 | } 105 | 106 | void SceneFilter::setNovel(Novel *novel) 107 | { 108 | mNovel = novel; 109 | emit filterChanged(); 110 | } 111 | -------------------------------------------------------------------------------- /src/app/scenefilter.h: -------------------------------------------------------------------------------- 1 | #ifndef SCENEFILTER_H 2 | #define SCENEFILTER_H 3 | 4 | #include 5 | #include 6 | #include "plotline.h" 7 | #include "sceneitemmodel.h" 8 | #include "plotlineitemmodel.h" 9 | 10 | class SceneFilter : public QSortFilterProxyModel 11 | { 12 | Q_OBJECT 13 | public: 14 | explicit SceneFilter(Novel *novel, Plotline *plotline = 0, QObject *parent = 0); 15 | 16 | 17 | Qt::ItemFlags flags(const QModelIndex &index) const; 18 | Qt::DropActions supportedDropActions() const; 19 | 20 | // int rowCount(const QModelIndex &parent = QModelIndex()) const; 21 | bool filterAcceptsRow(int source_row, 22 | const QModelIndex &source_parent = QModelIndex()) 23 | const; 24 | bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()); 25 | 26 | bool removeRows(const QModelIndexList &indexList); 27 | bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count, 28 | const QModelIndex &destinationParent, int destinationChild); 29 | 30 | Plotline *plotline() const; 31 | void setPlotline(Plotline *plotline); 32 | 33 | QString search() const; 34 | void setSearch(const QString &search); 35 | 36 | Novel *novel() const; 37 | void setNovel(Novel *novel); 38 | 39 | private: 40 | Novel *mNovel; 41 | Plotline *mPlotline; 42 | QString mSearch; 43 | 44 | signals: 45 | 46 | public slots: 47 | }; 48 | 49 | #endif // SCENEFILTER_H 50 | -------------------------------------------------------------------------------- /src/app/sceneframe.h: -------------------------------------------------------------------------------- 1 | #ifndef SCENEFRAME_H 2 | #define SCENEFRAME_H 3 | 4 | #include "plotlineappframe.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "sceneitemmodel.h" 11 | #include "modelcheckbox.h" 12 | #include "characterparser.h" 13 | #include "characterhighlighter.h" 14 | #include "utils.h" 15 | #include "scenefilter.h" 16 | 17 | namespace Ui { 18 | class SceneFrame; 19 | } 20 | 21 | class SceneFrame : public PlotlineAppFrame 22 | { 23 | Q_OBJECT 24 | 25 | public: 26 | explicit SceneFrame(MainWindow *mainWindow, QWidget *parent = 0); 27 | ~SceneFrame(); 28 | 29 | signals: 30 | 31 | public slots: 32 | void onNovelLoad(); 33 | void onNovelNew(); 34 | 35 | protected: 36 | 37 | private slots: 38 | void on_detectCharacters_clicked(); 39 | void on_sceneList_activated(const QModelIndex &index); 40 | void on_addScene_clicked(); 41 | void on_archiveScene_clicked(); 42 | void on_deleteScene_clicked(); 43 | void onCharacterToggled(bool checked, QVariant value); 44 | void on_sceneList_clicked(const QModelIndex &index); 45 | void on_plotline_activated(int index); 46 | // void on_sceneHeadline_textChanged(); 47 | // void on_sceneAction_textChanged(); 48 | void onHeadlineModified(); 49 | 50 | void onActionModified(); 51 | 52 | void on_sceneList_customContextMenuRequested(const QPoint &pos); 53 | 54 | void on_filterScenes_activated(int index); 55 | 56 | void on_searchScenes_textEdited(const QString &arg1); 57 | 58 | void on_sceneList_indexesMoved(const QModelIndexList &indexes); 59 | 60 | void on_characterSearch_textChanged(const QString &arg1); 61 | 62 | private: 63 | 64 | void disconnectSlots(); 65 | void connectSlots(); 66 | void blockEditableSignals(); 67 | void unblockEditableSignals(); 68 | 69 | QList _getSelectedCharacters(bool pov=false); 70 | void _setSelectedCharacters(QList characters, bool pov=false); 71 | 72 | QList getSelectedCharacters(); 73 | void setSelectedCharacters(QList characters); 74 | 75 | QList getSelectedPointsOfView(); 76 | void setSelectedPointsOfView(QList characters); 77 | 78 | void findCharacters(const QTextEdit *editor); 79 | void detectLabelStart(QTextEdit *editor); 80 | void fillPlotlineCombo(Plotline *selected = 0); 81 | 82 | Ui::SceneFrame *ui; 83 | 84 | Scene *mSelectedScene = 0; 85 | QList mCharacters; 86 | SceneFilter *mFilter; 87 | QPoint mDragPos; 88 | 89 | class HeadlineUpdater : public QRunnable 90 | { 91 | public: 92 | HeadlineUpdater(QTextEdit *field, QListView *listView); 93 | void run(); 94 | private: 95 | QTextEdit *mField; 96 | QListView *mListView; 97 | }; 98 | 99 | CharacterHighlighter *mActionHighlighter, 100 | *mHeadlineHighlighter; 101 | QCompleter *mHeadlineCompleter, 102 | *mActionCompleter; 103 | }; 104 | 105 | #endif // SCENEFRAME_H 106 | -------------------------------------------------------------------------------- /src/app/sceneitemmodel.h: -------------------------------------------------------------------------------- 1 | #ifndef SCENEITEMMODEL_H 2 | #define SCENEITEMMODEL_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include "scene.h" 8 | 9 | class SceneModel : public QAbstractListModel 10 | { 11 | public: 12 | SceneModel(Novel *novel, QObject *parent = 0 ); 13 | 14 | // Overrides 15 | int rowCount(const QModelIndex &parent = QModelIndex()) const; 16 | QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; 17 | bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::DisplayRole); 18 | QVariant headerData(int section, Qt::Orientation orientation, 19 | int role = Qt::DisplayRole) const; 20 | 21 | Qt::ItemFlags flags(const QModelIndex &index) const; 22 | 23 | bool insertRows(int row, int count, 24 | const QModelIndex & parent = QModelIndex()); 25 | bool insertRows(int row, int count, Plotline *plotline = 0, 26 | const QModelIndex & parent = QModelIndex()); 27 | bool removeRows(int row, int count, 28 | const QModelIndex & parent = QModelIndex()); 29 | 30 | bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count, 31 | const QModelIndex &destinationParent, int destinationChild); 32 | 33 | // Drag/drop support 34 | 35 | // bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, 36 | // int column, const QModelIndex &parent); 37 | Qt::DropActions supportedDropActions() const; 38 | // QStringList mimeTypes() const; 39 | 40 | static const int 41 | HeadlineRole = Qt::UserRole + 0, 42 | ActionRole = Qt::UserRole + 1, 43 | PlotlineRole = Qt::UserRole + 2, 44 | CharactersRole = Qt::UserRole + 3, 45 | PointsOfViewRole = Qt::UserRole + 4, 46 | IdRole = Qt::UserRole + 5; 47 | 48 | private: 49 | 50 | Novel *mNovel; 51 | 52 | signals: 53 | 54 | public slots: 55 | }; 56 | 57 | #endif // SCENEITEMMODEL_H 58 | -------------------------------------------------------------------------------- /src/app/scenelistdialog.cpp: -------------------------------------------------------------------------------- 1 | #include "scenelistdialog.h" 2 | #include "ui_scenelistdialog.h" 3 | 4 | SceneListDialog::SceneListDialog(Novel *novel, QTableView *chapterTable, QWidget *parent) : 5 | QDialog(parent), 6 | ui(new Ui::SceneListDialog) 7 | { 8 | ui->setupUi(this); 9 | 10 | mNovel = novel; 11 | mChapterTable = chapterTable; 12 | 13 | // Get the chapter from the table. 14 | QModelIndex index = chapterTable->currentIndex(); 15 | ChapterModel *model = (ChapterModel *) chapterTable->model(); 16 | QUuid id = model->data(index, ChapterModel::IdRole).toUuid(); 17 | Chapter *chapter = mNovel->chapter(id); 18 | QList plotlines = mNovel->plotlines(); 19 | 20 | if (!chapter){ 21 | qWarning() << "No chapter selected."; 22 | return; 23 | } 24 | 25 | // Fill the plotline selection 26 | ui->plotlineSelection->addItem("", QVariant(0)); 27 | for (Plotline *p : plotlines) 28 | ui->plotlineSelection->addItem(p->brief(), p->id()); 29 | 30 | // Get the scene lists to compare. 31 | QList novelScenes = mNovel->scenes(); 32 | QList chapterScenes = chapter->scenes(); 33 | 34 | // Add all novel scenes as a model checkbox. If the scene is included 35 | // as a scene assigned to the chapter, mark it as checked. 36 | for (Scene *s : novelScenes){ 37 | ModelCheckbox *cb = new ModelCheckbox(s->headline(), s->id()); 38 | cb->setChecked(chapterScenes.contains(s)); 39 | mCheckboxes << cb; 40 | ui->sceneList->addWidget(cb); 41 | } 42 | 43 | fillList(); 44 | } 45 | 46 | void SceneListDialog::accept(){ 47 | QAbstractItemModel *model = mChapterTable->model(); 48 | QModelIndex index = mChapterTable->currentIndex(); 49 | int role = 0; 50 | for (ModelCheckbox *cb : mCheckboxes){ 51 | QUuid id = cb->value().toUuid(); 52 | if (cb->isChecked()) 53 | role = ChapterModel::AddSceneRole; 54 | else 55 | role = ChapterModel::RemoveSceneRole; 56 | model->setData(index, id, role); 57 | } 58 | this->close(); 59 | } 60 | 61 | SceneListDialog::~SceneListDialog() 62 | { 63 | delete ui; 64 | } 65 | 66 | void SceneListDialog::fillList() 67 | { 68 | Scene *s = 0; 69 | 70 | for (ModelCheckbox *cb : mCheckboxes){ 71 | s = mNovel->scene(cb->value().toUuid()); 72 | bool contains = s->headline().toLower().contains(mSearch.toLower()); 73 | bool plotlineMatches = (s->plotline() == mPlotline); 74 | if ((mPlotline == 0 || plotlineMatches) && (mSearch.isNull() || contains)) 75 | cb->setVisible(true); 76 | else 77 | cb->setVisible(false); 78 | } 79 | } 80 | 81 | void SceneListDialog::on_plotlineSelection_activated(int index) 82 | { 83 | if (index == 0){ 84 | mPlotline = 0; 85 | fillList(); 86 | } 87 | QUuid id = ui->plotlineSelection->currentData().toUuid(); 88 | mPlotline = mNovel->plotline(id); 89 | fillList(); 90 | } 91 | 92 | void SceneListDialog::on_sceneSearch_textChanged(const QString &arg1) 93 | { 94 | mSearch = arg1; 95 | fillList(); 96 | } 97 | -------------------------------------------------------------------------------- /src/app/scenelistdialog.h: -------------------------------------------------------------------------------- 1 | #ifndef SCENELISTDIALOG_H 2 | #define SCENELISTDIALOG_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "modelcheckbox.h" 9 | #include "mainwindow.h" 10 | #include "chaptermodel.h" 11 | #include "utils.h" 12 | 13 | namespace Ui { 14 | class SceneListDialog; 15 | } 16 | 17 | class SceneListDialog : public QDialog 18 | { 19 | Q_OBJECT 20 | 21 | public: 22 | explicit SceneListDialog(Novel *novel, QTableView *mChapterTable, QWidget *parent = 0); 23 | void accept(); 24 | ~SceneListDialog(); 25 | 26 | private slots: 27 | 28 | void on_plotlineSelection_activated(int index); 29 | 30 | void on_sceneSearch_textChanged(const QString &arg1); 31 | 32 | private: 33 | Ui::SceneListDialog *ui; 34 | 35 | Novel *mNovel; 36 | QTableView *mChapterTable; 37 | QList mCheckboxes; 38 | 39 | Plotline *mPlotline = 0; 40 | QString mSearch; 41 | 42 | void fillList(); 43 | }; 44 | 45 | #endif // SCENELISTDIALOG_H 46 | -------------------------------------------------------------------------------- /src/app/scenelistdialog.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | SceneListDialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 400 10 | 300 11 | 12 | 13 | 14 | Dialog 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 2 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | Search for scenes... 34 | 35 | 36 | 37 | 38 | 39 | 40 | true 41 | 42 | 43 | 44 | 45 | 0 46 | 0 47 | 376 48 | 189 49 | 50 | 51 | 52 | 53 | QLayout::SetMinimumSize 54 | 55 | 56 | 0 57 | 58 | 59 | 0 60 | 61 | 62 | 0 63 | 64 | 65 | 0 66 | 67 | 68 | 69 | 70 | 6 71 | 72 | 73 | QLayout::SetMinimumSize 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | Qt::Horizontal 87 | 88 | 89 | QDialogButtonBox::Cancel|QDialogButtonBox::Ok 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | buttonBox 101 | accepted() 102 | SceneListDialog 103 | accept() 104 | 105 | 106 | 248 107 | 254 108 | 109 | 110 | 157 111 | 274 112 | 113 | 114 | 115 | 116 | buttonBox 117 | rejected() 118 | SceneListDialog 119 | reject() 120 | 121 | 122 | 316 123 | 260 124 | 125 | 126 | 286 127 | 274 128 | 129 | 130 | 131 | 132 | 133 | -------------------------------------------------------------------------------- /src/app/styleproxy.cpp: -------------------------------------------------------------------------------- 1 | #include "styleproxy.h" 2 | 3 | StyleProxy::StyleProxy(bool bold, bool italic, int relSize, 4 | QColor foreground, 5 | QColor background, 6 | QObject *parent) : QObject(parent) 7 | { 8 | mBold = bold; 9 | mItallic = italic; 10 | mRelSize = relSize; 11 | mForeground = foreground; 12 | mBackground = background; 13 | } 14 | 15 | bool StyleProxy::bold() const 16 | { 17 | return mBold; 18 | } 19 | 20 | void StyleProxy::setBold(bool bold) 21 | { 22 | mBold = bold; 23 | } 24 | 25 | bool StyleProxy::itallic() const 26 | { 27 | return mItallic; 28 | } 29 | 30 | void StyleProxy::setItallic(bool itallic) 31 | { 32 | mItallic = itallic; 33 | } 34 | 35 | int StyleProxy::relSize() const 36 | { 37 | return mRelSize; 38 | } 39 | 40 | void StyleProxy::setRelSize(int relSize) 41 | { 42 | mRelSize = relSize; 43 | } 44 | 45 | QColor StyleProxy::foreground() const 46 | { 47 | return mForeground; 48 | } 49 | 50 | void StyleProxy::setForeground(const QColor &foreground) 51 | { 52 | mForeground = foreground; 53 | } 54 | 55 | QColor StyleProxy::background() const 56 | { 57 | return mBackground; 58 | } 59 | 60 | void StyleProxy::setBackground(const QColor &background) 61 | { 62 | mBackground = background; 63 | } 64 | 65 | StyleProxy *StyleProxy::parse(QJsonObject object) 66 | { 67 | bool bold = false, italic = false; 68 | int relSize = 0; 69 | QColor foreground, background; 70 | 71 | if (object.contains("bold")){ 72 | bold = object["bold"].toBool(); 73 | // qDebug() << " found bold=" << bold; 74 | }if (object.contains("italic")){ 75 | italic = object["italic"].toBool(); 76 | // qDebug() << " found italic=" << italic; 77 | }if (object.contains("relSize")){ 78 | relSize = object["relSize"].toInt(); 79 | // qDebug() << " found relSize=" << relSize; 80 | }if (object.contains("foreground") && !object["foreground"].isNull()){ 81 | if (object["foreground"].isArray()){ 82 | QJsonArray arr = object["foreground"].toArray(); 83 | int r = arr[0].toInt(), g = arr[1].toInt(), b = arr[2].toInt(), 84 | a = arr[3].toInt(); 85 | foreground = QColor(r, g, b, a); 86 | } else { 87 | foreground = QColor(object["background"].toString()); 88 | } 89 | 90 | if (!foreground.isValid()) 91 | qWarning() << "Invalid fg color:" << object["background"].toString(); 92 | qDebug() << " found foreground=" << foreground; 93 | } 94 | if (object.contains("background") && !object["background"].isNull()){ 95 | if (object["background"].isArray()){ 96 | QJsonArray arr = object["background"].toArray(); 97 | int r = arr[0].toInt(), g = arr[1].toInt(), b = arr[2].toInt(), 98 | a = arr[3].toInt(); 99 | background = QColor(r, g, b, a); 100 | } else { 101 | background = QColor(object["background"].toString()); 102 | } 103 | if (!background.isValid()) 104 | qWarning() << "Invalid bg color:" << object["foreground"].toString(); 105 | // qDebug() << " found background=" << background; 106 | } 107 | 108 | // qDebug() << "Leaving styleProxy::parse"; 109 | 110 | return new StyleProxy(bold, italic, relSize, foreground, background); 111 | } 112 | 113 | QTextCharFormat StyleProxy::toFormat(const QTextCharFormat &base) const 114 | { 115 | QTextCharFormat fmt; 116 | 117 | QFont font = base.font(); 118 | 119 | font.setBold(mBold); 120 | font.setItalic(mItallic); 121 | font.setPointSize(font.pointSize() + mRelSize); 122 | fmt.setFont(font); 123 | 124 | if (mForeground.isValid()) 125 | fmt.setForeground(QBrush(mForeground)); 126 | if (mBackground.isValid()) 127 | fmt.setBackground(QBrush(mBackground)); 128 | 129 | // qDebug() << "Entering toFormat"; 130 | // qDebug() << "is bold?" << fmt.font().bold(); 131 | // qDebug() << "is Italic?" << fmt.font().italic(); 132 | // qDebug() << "point size:" << fmt.font().pointSize(); 133 | 134 | return fmt; 135 | } 136 | 137 | -------------------------------------------------------------------------------- /src/app/styleproxy.h: -------------------------------------------------------------------------------- 1 | #ifndef STYLEPROXY_H 2 | #define STYLEPROXY_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | class StyleProxy : public QObject 9 | { 10 | Q_OBJECT 11 | public: 12 | explicit StyleProxy(bool bold=false, bool itallic=false, int relSize=0, 13 | QColor foreground=QColor(), 14 | QColor background=QColor(), 15 | QObject *parent = 0); 16 | 17 | bool bold() const; 18 | void setBold(bool bold); 19 | 20 | bool itallic() const; 21 | void setItallic(bool itallic); 22 | 23 | int relSize() const; 24 | void setRelSize(int relSize); 25 | 26 | QColor foreground() const; 27 | void setForeground(const QColor &foreground); 28 | 29 | QColor background() const; 30 | void setBackground(const QColor &background); 31 | 32 | static StyleProxy* parse(QJsonObject object); 33 | QTextCharFormat toFormat(const QTextCharFormat &base) const; 34 | 35 | private: 36 | bool mBold, mItallic; 37 | int mRelSize; 38 | QColor mForeground, mBackground; 39 | 40 | signals: 41 | 42 | public slots: 43 | }; 44 | 45 | #endif // STYLEPROXY_H 46 | -------------------------------------------------------------------------------- /src/app/styleproxyparser.cpp: -------------------------------------------------------------------------------- 1 | #include "styleproxyparser.h" 2 | 3 | StyleProxyParser::StyleProxyParser(const QJsonObject &object) 4 | { 5 | mObject = object; 6 | } 7 | 8 | StyleProxy *StyleProxyParser::parse(const QString &key) const 9 | { 10 | if (!mObject.contains(key)){ 11 | // qWarning() << "Missing key" << key; 12 | return 0; 13 | } 14 | // qDebug() << "Entering SyleProxy::parse for key" << key; 15 | return StyleProxy::parse(mObject[key].toObject()); 16 | } 17 | 18 | StyleProxy *StyleProxyParser::parse(const char *key) const 19 | { 20 | return parse(QString(key)); 21 | } 22 | 23 | QJsonObject StyleProxyParser::object() const 24 | { 25 | return mObject; 26 | } 27 | 28 | void StyleProxyParser::setObject(const QJsonObject &object) 29 | { 30 | mObject = object; 31 | } 32 | 33 | -------------------------------------------------------------------------------- /src/app/styleproxyparser.h: -------------------------------------------------------------------------------- 1 | #ifndef STYLEPROXYPARSER_H 2 | #define STYLEPROXYPARSER_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | class StyleProxyParser 9 | { 10 | public: 11 | StyleProxyParser(const QJsonObject &object=QJsonObject()); 12 | 13 | StyleProxy *parse(const QString &key) const; 14 | StyleProxy *parse(const char *key) const; 15 | 16 | QJsonObject object() const; 17 | void setObject(const QJsonObject &object); 18 | 19 | private: 20 | QJsonObject mObject; 21 | }; 22 | 23 | #endif // STYLEPROXYPARSER_H 24 | -------------------------------------------------------------------------------- /src/data/character-worthiness.json: -------------------------------------------------------------------------------- 1 | { 2 | "label" : "Novel Worthiness", 3 | "fields" : [ 4 | { 5 | "label" : "Main Character", 6 | "type" : "choice", 7 | "choices" : [ 8 | { 9 | "label" : "Heroic" 10 | }, 11 | { 12 | "label" : "Courageous" 13 | }, 14 | { 15 | "label" : "Goal-driven" 16 | }, 17 | { 18 | "label" : "Focused" 19 | }, 20 | { 21 | "label" : "Passionate" 22 | }, 23 | { 24 | "label" : "Sympathetic" 25 | }, 26 | { 27 | "label" : "Loyal" 28 | }, 29 | { 30 | "label" : "Attractive" 31 | }, 32 | { 33 | "label" : "Clever" 34 | }, 35 | { 36 | "label" : "Funny" 37 | }, 38 | { 39 | "label" : "Resourceful" 40 | }, 41 | { 42 | "label" : "Responds actively to surroundings" 43 | }, 44 | { 45 | "label" : "Embraces opportunity" 46 | }, 47 | { 48 | "label" : "Makes things happen" 49 | }, 50 | { 51 | "label" : "Mouthy" 52 | }, 53 | { 54 | "label" : "Stubborn" 55 | }, 56 | { 57 | "label" : "Perseveres with dignity" 58 | }, 59 | { 60 | "label" : "Stoic about problems" 61 | }, 62 | { 63 | "label" : "Leads Action" 64 | }, 65 | { 66 | "label" : "Idealistic" 67 | }, 68 | { 69 | "label" : "High Opinion of Self" 70 | }, 71 | { 72 | "label" : "Lives by Different Rules", 73 | "name" : "different_rules" 74 | }, 75 | { 76 | "label" : "Quintessentially", 77 | "name" : "quintessentially" 78 | }, 79 | { 80 | "label" : "Essential to the plot" 81 | }, 82 | { 83 | "label" : "Responsible for resolution" 84 | } 85 | ], 86 | "special" : [ 87 | { 88 | "field" : "different_rules", 89 | "type" : "small-string" 90 | }, 91 | { 92 | "field" : "quintessentially", 93 | "type" : "small-string" 94 | } 95 | ] 96 | }, 97 | { 98 | "label" : "Sub-Ordinate Character", 99 | "type" : "choice", 100 | "choices" : [ 101 | { 102 | "label" : "Reinforces a main character" 103 | }, 104 | { 105 | "label" : "Encourages and supports main character" 106 | }, 107 | { 108 | "label" : "Adds humor" 109 | }, 110 | { 111 | "label" : "Brings out Protag’s Strength" 112 | }, 113 | { 114 | "label" : "Piles on complications" 115 | } 116 | ] 117 | } 118 | 119 | ] 120 | } 121 | -------------------------------------------------------------------------------- /src/data/novel-overview.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "overview", 3 | "label" : "Overview", 4 | "attributes" : [ 5 | { 6 | "label" : "Working Title", 7 | "type" : String, 8 | "isLong" : false 9 | }, 10 | { 11 | "label" : "Genre", 12 | "type" : String, 13 | "isLong" : false 14 | }, 15 | { 16 | "label" : "Tense/POV", 17 | "type" : String, 18 | "isLong" : false 19 | }, 20 | { 21 | "label" : "Setting", 22 | "type" : String 23 | } 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /src/data/novel-part-1.json: -------------------------------------------------------------------------------- 1 | { 2 | "label" : "Part 1", 3 | "attributes" : [ 4 | { 5 | "label" : "Inciting Incident", 6 | "prompt" : "What happens to the protagonist to put her unavoidably in the path of the antagonist?", 7 | "type" : String 8 | }, 9 | { 10 | "label" : "Internal Initial Conflict (call to action)", 11 | "name" : "ii_conflict", 12 | "prompt" : "What does your protagonist most want? Why can’t she have it? How will she try to get it?", 13 | "type" : String 14 | }, 15 | { 16 | "label" : "External Initial Conflict (call to action)", 17 | "name" : "ei_conflict", 18 | "prompt" : "What does your protagonist want to accomplish or obtain (physically)? How will she go about it?", 19 | "type" : String 20 | }, 21 | { 22 | "label": "Woven-in Backstory, Vital Information", 23 | "name" : "backstory", 24 | "prompt": "What happened before the inciting incident that we must know to understand the story?", 25 | "type" : String 26 | }, 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /src/data/novel-part-2.json: -------------------------------------------------------------------------------- 1 | { 2 | "label" : "Part 2", 3 | "attributes" : [ 4 | { 5 | "label": "Internal Conflict (obstacles)", 6 | "prompt": "Why is your protagonist hesitant to strive for her goals? What (emotionally) makes her falter?", 7 | "type": String 8 | }, 9 | { 10 | "label": "External Conflict (obstacles)", 11 | "prompt" : "What stands in the way of your protagonist’s goals? What will happen to her if she fails?", 12 | "type" : String 13 | }, 14 | { 15 | "label": "Internal Higher Conflict (obstacles heighten)", 16 | "prompt": "Why should your protagonist turn back now? Why doesn’t she? What’s at stake?", 17 | "type" : String 18 | }, 19 | { 20 | "label" : "External Higher Conflict (obstacles heighten)", 21 | "prompt" : "Who or what is trying to stop your protagonist? Why?", 22 | "type" : String 23 | }, 24 | { 25 | "label" : "Internal Highest Conflict (obstacles intensify)", 26 | "prompt" : "What makes your protagonist realize the unavoidable importance of her original goal? What gives it new meaning?", 27 | "type" : String 28 | }, 29 | { 30 | "label" : "External Highest Conflict (obstacles intensify)", 31 | "prompt" : "How does the antagonist get the best of your protagonist? What could make it worse? What happens to make her believe that there is no way to win?", 32 | "type" : String 33 | }, 34 | { 35 | "label" : "Internal Point of No Return (stakes)", 36 | "prompt" : "What happens to change your protagonist so that she’ll never be the same again?", 37 | "type" : String 38 | }, 39 | { 40 | "label" : "External Point of No Return (stakes)", 41 | "prompt" : "What makes it impossible for your protagonist to go back, to give up?", 42 | "type" : String 43 | }, 44 | { 45 | "label" : "Darkest Hour", 46 | "prompt" : "What is the worst possible thing that could happen to your protagonist?", 47 | "type" : String 48 | }, 49 | { 50 | "label": "Turning Point", 51 | "prompt" : "How does your protagonist realize she must continue to fight? How does she decide to risk everything? What new approach or idea has she come up with to battle on?", 52 | "type" : String 53 | } 54 | ], 55 | } 56 | -------------------------------------------------------------------------------- /src/data/novel-part-3.json: -------------------------------------------------------------------------------- 1 | { 2 | "label" : "Part 3", 3 | "attributes" : [ 4 | { 5 | "label" : "Internal Climax", 6 | "prompt" : "What does your protagonist realize at the crucial moment? What does she learn? Overcome?", 7 | "type" : String, 8 | }, 9 | { 10 | "label" : "External Climax", 11 | "prompt" : "How does your protagonist defeat your antagonist?", 12 | "type" : String 13 | }, 14 | { 15 | "label" : "Resolution (external)", 16 | "prompt" : "What does defeating the antagonist accomplish? How are things different?", 17 | "type" : String 18 | }, 19 | { 20 | "label" : "Character Growth (internal)", 21 | "prompt" : "How has your protagonist changed?", 22 | "type" : String 23 | } 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /src/lib/author.cpp: -------------------------------------------------------------------------------- 1 | #include "author.h" 2 | 3 | const QString Author::J_NAME = "name", 4 | Author::J_PENNAME = "penName", 5 | Author::J_EMAIL = "email", 6 | Author::J_ADDRESS = "address", 7 | Author::J_PHONE = "phone"; 8 | 9 | Author::Author(const QString &name, const QString &penName, 10 | const QString &email, const QString &address, 11 | const QString &phone, Novel *novel, const QUuid &id, QObject *parent) 12 | : QObject(parent), Serializable(id) 13 | { 14 | mName = name; 15 | mPenName = penName; 16 | mEmail = email; 17 | mAddress = address; 18 | mPhone = phone; 19 | mNovel = novel; 20 | } 21 | 22 | QJsonObject Author::serialize() const 23 | { 24 | QJsonObject obj; 25 | 26 | obj[JSON_ID] = id().toString(); 27 | obj[J_NAME] = mName; 28 | obj[J_PENNAME] = mPenName; 29 | obj[J_EMAIL] = mEmail; 30 | obj[J_ADDRESS] = mAddress; 31 | obj[J_PHONE] = mPhone; 32 | 33 | return obj; 34 | } 35 | 36 | Author *Author::deserialize(Novel *novel, const QJsonObject &object) 37 | { 38 | return new Author(object.value(J_NAME).toString(), 39 | object.value(J_PENNAME).toString(), 40 | object.value(J_EMAIL).toString(), 41 | object.value(J_ADDRESS).toString(), 42 | object.value(J_PHONE).toString(), 43 | novel, 44 | QUuid(object.value(JSON_ID).toString())); 45 | } 46 | 47 | QString Author::name() const 48 | { 49 | return mName; 50 | } 51 | 52 | void Author::setName(const QString &name) 53 | { 54 | mName = name; 55 | } 56 | 57 | QString Author::penName() const 58 | { 59 | return mPenName; 60 | } 61 | 62 | void Author::setPenName(const QString &penName) 63 | { 64 | mPenName = penName; 65 | } 66 | 67 | QString Author::email() const 68 | { 69 | return mEmail; 70 | } 71 | 72 | void Author::setEmail(const QString &email) 73 | { 74 | mEmail = email; 75 | } 76 | 77 | QString Author::address() const 78 | { 79 | return mAddress; 80 | } 81 | 82 | void Author::setAddress(const QString &address) 83 | { 84 | mAddress = address; 85 | } 86 | 87 | QString Author::phone() const 88 | { 89 | return mPhone; 90 | } 91 | 92 | void Author::setPhone(const QString &phone) 93 | { 94 | mPhone = phone; 95 | } 96 | 97 | Novel *Author::novel() const 98 | { 99 | return mNovel; 100 | } 101 | 102 | void Author::setNovel(Novel *novel) 103 | { 104 | mNovel = novel; 105 | } 106 | 107 | -------------------------------------------------------------------------------- /src/lib/author.h: -------------------------------------------------------------------------------- 1 | #ifndef AUTHOR_H 2 | #define AUTHOR_H 3 | 4 | #include 5 | #include "serializable.h" 6 | 7 | class Novel; 8 | 9 | class Author : public QObject, public Serializable 10 | { 11 | Q_OBJECT 12 | public: 13 | explicit Author(const QString &name = QString(), 14 | const QString &penName = QString(), 15 | const QString &email = QString(), 16 | const QString &address = QString(), 17 | const QString &phone = QString(), 18 | Novel *novel = 0, 19 | const QUuid &id = 0, 20 | QObject *parent = 0); 21 | 22 | QJsonObject serialize() const; 23 | static Author *deserialize(Novel *novel, const QJsonObject &object); 24 | 25 | QString name() const; 26 | void setName(const QString &name); 27 | 28 | QString penName() const; 29 | void setPenName(const QString &penName); 30 | 31 | QString email() const; 32 | void setEmail(const QString &email); 33 | 34 | QString address() const; 35 | void setAddress(const QString &address); 36 | 37 | QString phone() const; 38 | void setPhone(const QString &phone); 39 | 40 | Novel *novel() const; 41 | void setNovel(Novel *novel); 42 | 43 | private: 44 | QString mName, mPenName, mEmail, mAddress, mPhone; 45 | Novel *mNovel; 46 | 47 | static const QString J_NAME, J_PENNAME, J_EMAIL, J_ADDRESS, J_PHONE; 48 | 49 | signals: 50 | 51 | public slots: 52 | }; 53 | 54 | #include "novel.h" 55 | 56 | #endif // AUTHOR_H 57 | -------------------------------------------------------------------------------- /src/lib/chapter.h: -------------------------------------------------------------------------------- 1 | #ifndef CHAPTER_H 2 | #define CHAPTER_H 3 | 4 | #include 5 | #include "completable.h" 6 | #include "scene.h" 7 | #include "revision.h" 8 | 9 | #include "serializable.h" 10 | 11 | class Novel; 12 | class Scene; 13 | class Revision; 14 | 15 | class Chapter : public QObject, public Serializable 16 | { 17 | private: 18 | QString mTitle; 19 | QList mScenes; 20 | QList mRevisions; 21 | Novel *mNovel; 22 | 23 | int mCurrentRevision = 0; 24 | 25 | static const QString JSON_TITLE, JSON_REVISIONS, 26 | JSON_SCENES, JSON_CURRENT_REVISION; 27 | 28 | Q_OBJECT 29 | public: 30 | explicit Chapter(const QString &title = QString(), 31 | const QList &revisions = QList(), 32 | const QList &scenes = QList(), 33 | Novel *novel = 0, 34 | int currentRevision = -1, 35 | QUuid id = QUuid(), 36 | QObject *parent = 0); 37 | ~Chapter(); 38 | 39 | QString title() const; 40 | void setTitle(const QString &title); 41 | 42 | QList scenes() const; 43 | void setScenes(const QList &scenes); 44 | 45 | Novel *novel() const; 46 | void setNovel(Novel *novel); 47 | 48 | QJsonObject serialize() const; 49 | static Chapter *deserialize(Novel *novel, const QJsonObject &object); 50 | static QList deserialize(Novel *novel, const QJsonArray &object); 51 | 52 | int number(); 53 | QList revisions() const; 54 | Revision *addRevision(); 55 | void removeRevision(int i); 56 | int currentRevision() const; 57 | void setCurrentRevision(int currentRevision); 58 | void setContent(const QString &content, const int revision=-1); 59 | void setIsComplete(const bool complete, const int revision=-1); 60 | bool canMarkCompleted(const int revision=-1) const; 61 | 62 | QString content(int revision) const; 63 | QString currentContent() const; 64 | QString latestContent() const; 65 | 66 | signals: 67 | 68 | public slots: 69 | }; 70 | 71 | #endif // CHAPTER_H 72 | -------------------------------------------------------------------------------- /src/lib/character.h: -------------------------------------------------------------------------------- 1 | #ifndef CHARACTER_H 2 | #define CHARACTER_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "serializable.h" 10 | #include "scene.h" 11 | #include "utils.h" 12 | 13 | class Scene; 14 | class Novel; 15 | 16 | class Character : public QObject, public Serializable 17 | { 18 | Q_OBJECT 19 | 20 | private: 21 | 22 | QString mName, 23 | mNickname, 24 | mLabel; 25 | QImage mHeadshot; 26 | QColor mColor; 27 | bool mIsArchived; 28 | 29 | static const QString J_NAME, 30 | J_NICKNAME, 31 | J_LABEL, 32 | J_HEADSHOT, 33 | J_COLOR, 34 | J_IS_ARCHIVED; 35 | 36 | Novel *mNovel; 37 | 38 | bool matches(const QString &string, const QRegularExpression &pattern); 39 | 40 | public: 41 | explicit Character(const QString &name, 42 | const QString &nickname = QString(), 43 | const QString &label = QString(), 44 | const QImage &headshot = QImage(), 45 | const QColor &color = QColor(), 46 | bool isArchived = false, 47 | Novel *novel = 0, 48 | QUuid id = QUuid(), 49 | QObject *parent = 0); 50 | ~Character(); 51 | 52 | static QString generateLabel(const QString &name); 53 | 54 | QString name() const; 55 | bool nameMatches(const QRegularExpression &pattern); 56 | 57 | QString nickname() const; 58 | 59 | QString label() const; 60 | bool labelMatches(const QRegularExpression &pattern); 61 | 62 | QImage getHeadshot() const; 63 | void setHeadshot(const QImage &value); 64 | 65 | QColor color() const; 66 | void setColor(const QColor &value); 67 | 68 | Novel *getNovel() const; 69 | void setNovel(Novel *novel); 70 | 71 | QJsonObject serialize() const; 72 | static Character *deserialize(Novel *novel, const QJsonObject &object); 73 | static QList deserialize(Novel *novel, const QJsonArray &object); 74 | 75 | bool getIsArchived() const; 76 | void setIsArchived(bool isArchived); 77 | 78 | static const QRegularExpression LABEL_X; 79 | 80 | signals: 81 | 82 | public slots: 83 | void setName(const QString &value); 84 | void setNickname(const QString &value); 85 | void setLabel(const QString &value); 86 | }; 87 | 88 | #endif // CHARACTER_H 89 | -------------------------------------------------------------------------------- /src/lib/characterparser.cpp: -------------------------------------------------------------------------------- 1 | #include "characterparser.h" 2 | 3 | ParsedCharacterSet::ParsedCharacterSet(QObject *parent) 4 | : QHash() 5 | { 6 | Q_UNUSED(parent); 7 | } 8 | 9 | ParsedCharacterSet::~ParsedCharacterSet() 10 | { 11 | 12 | } 13 | 14 | ParsedCharacterSet ParsedCharacterSet::parse(Novel *novel, const QString &string) 15 | { 16 | int index = 0; 17 | QString label; 18 | Character *character = 0; 19 | 20 | ParsedCharacterSet set = ParsedCharacterSet(); 21 | 22 | QRegularExpression labelX = Character::LABEL_X; 23 | 24 | QRegularExpressionMatchIterator matches = labelX.globalMatch(string); 25 | 26 | QRegularExpressionMatch match; 27 | while (matches.hasNext()){ 28 | match = matches.next(); 29 | label = match.captured(1); 30 | index = match.capturedStart(1); 31 | character = novel->characterByLabel(label); 32 | if (character) 33 | set.insert(index-1, character); 34 | } 35 | 36 | 37 | return set; 38 | } 39 | 40 | -------------------------------------------------------------------------------- /src/lib/characterparser.h: -------------------------------------------------------------------------------- 1 | #ifndef CHARACTERPARSER_H 2 | #define CHARACTERPARSER_H 3 | 4 | #include 5 | 6 | class Character; 7 | class Novel; 8 | 9 | class ParsedCharacterSet : public QHash 10 | { 11 | 12 | public: 13 | ParsedCharacterSet(QObject *parent = 0); 14 | ~ParsedCharacterSet(); 15 | 16 | static ParsedCharacterSet parse(Novel *novel, const QString &string); 17 | 18 | signals: 19 | 20 | public slots: 21 | }; 22 | 23 | #include "character.h" 24 | #include "novel.h" 25 | 26 | #endif // CHARACTERPARSER_H 27 | -------------------------------------------------------------------------------- /src/lib/completable.cpp: -------------------------------------------------------------------------------- 1 | #include "completable.h" 2 | 3 | Completable::Completable(QObject *parent) : QObject(parent) 4 | { 5 | this->mComplete = false; 6 | } 7 | 8 | bool Completable::isCompleted() 9 | { 10 | return this->isCompleted(); 11 | } 12 | 13 | void Completable::markCompleted() 14 | { 15 | this->mComplete = true; 16 | } 17 | 18 | void Completable::unmarkCompleted() 19 | { 20 | this->mComplete = false; 21 | } 22 | 23 | void Completable::toggleCompleted() 24 | { 25 | this->mComplete = !this->mComplete; 26 | } 27 | 28 | void Completable::setCompleted(int state) 29 | { 30 | this->mComplete = (state == Qt::Checked); 31 | } 32 | -------------------------------------------------------------------------------- /src/lib/completable.h: -------------------------------------------------------------------------------- 1 | #ifndef COMPLETABLE_H 2 | #define COMPLETABLE_H 3 | 4 | #include 5 | 6 | class Completable : public QObject 7 | { 8 | Q_OBJECT 9 | public: 10 | explicit Completable(QObject *parent = 0); 11 | 12 | bool isCompleted(); 13 | void markCompleted(); 14 | void unmarkCompleted(); 15 | void toggleCompleted(); 16 | 17 | protected: 18 | bool mComplete; 19 | 20 | signals: 21 | 22 | public slots: 23 | void setCompleted(int state); 24 | }; 25 | 26 | #endif // COMPLETABLE_H 27 | -------------------------------------------------------------------------------- /src/lib/lib.pro: -------------------------------------------------------------------------------- 1 | 2 | TEMPLATE = lib 3 | TARGET = plotline 4 | QT += core gui widgets 5 | DEFINES += LIBPLOTLINE 6 | 7 | LIBPLOTLINE.path = $$OUT_PWD/ 8 | 9 | HEADERS += chapter.h \ 10 | character.h \ 11 | completable.h \ 12 | novel.h \ 13 | plotline.h \ 14 | scene.h \ 15 | serializable.h \ 16 | revision.h \ 17 | utils.h \ 18 | characterparser.h \ 19 | author.h 20 | 21 | SOURCES += chapter.cpp \ 22 | character.cpp \ 23 | completable.cpp \ 24 | novel.cpp \ 25 | plotline.cpp \ 26 | scene.cpp \ 27 | serializable.cpp \ 28 | revision.cpp \ 29 | utils.cpp \ 30 | characterparser.cpp \ 31 | author.cpp 32 | 33 | INSTALLS += LIBPLOTLINE 34 | 35 | CONFIG += c++11 36 | 37 | CONFIG += -Wl,-export-all-symbols 38 | win:LIBS += -lws2_32 39 | -------------------------------------------------------------------------------- /src/lib/novel.h: -------------------------------------------------------------------------------- 1 | #ifndef NOVEL_H 2 | #define NOVEL_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include "plotline.h" 8 | #include "chapter.h" 9 | #include "serializable.h" 10 | #include "revision.h" 11 | #include "author.h" 12 | 13 | class Chapter; 14 | class Plotline; 15 | class Character; 16 | class Scene; 17 | class Revision; 18 | class Author; 19 | 20 | class Novel : public QObject, public Serializable 21 | { 22 | Q_OBJECT 23 | 24 | public: 25 | enum Tense { 26 | Past=0, Present, Future, OtherTense, 27 | }; 28 | 29 | enum PointOfView { 30 | FirstPersonSingular=0, SecondPersonSingular, ThirdPersonSingular, 31 | FirstPersonPlural, SecondPersonPlural, ThirdPersonPlural, 32 | OtherPointOfView, 33 | }; 34 | 35 | explicit Novel(const QString &mWorkingTitle, 36 | const QString &mGenre = QString(), 37 | const QString &mSetting = QString(), 38 | const Novel::Tense tense = Past, 39 | const Novel::PointOfView pov = ThirdPersonSingular, 40 | Author *author = 0, 41 | const QList characters = QList(), 42 | const QList scenes = QList(), 43 | const QList chapters = QList(), 44 | const QList plotlines = QList(), 45 | const QStringList &revisions = QStringList(), 46 | int currentRevision = -1, 47 | QUuid id = QUuid(), 48 | QObject *parent = 0); 49 | ~Novel(); 50 | 51 | QString getWorkingTitle() const; 52 | 53 | QString getGenre() const; 54 | void setWorkingTitle(const QString &value); 55 | void setGenre(const QString &value); 56 | void setSetting(const QString &value); 57 | 58 | Novel::Tense getTense() const; 59 | void setTense(const Novel::Tense tense); 60 | 61 | QString getSetting() const; 62 | 63 | QList scenes() const; 64 | void setScenes(const QList &value); 65 | void addScene(Scene *scene, int i=-1); 66 | void removeScene(Scene *scene); 67 | void removeScene(QUuid id); 68 | Scene *scene(QUuid id) const; 69 | void moveScenes(int from, int to); 70 | 71 | QList chapters() const; 72 | Chapter *chapterByNumber(int number); 73 | Chapter *chapter(QUuid id) const; 74 | void setChapters(const QList &value); 75 | void addChapter(Chapter *chapter, int loc = -1); 76 | Revision *chapterRevision(int chapter, int revision); 77 | void removeChapter(Chapter *chapter); 78 | void removeChapterAt(int index, bool doDelete=false); 79 | void moveChapter(int from, int to); 80 | 81 | Novel::PointOfView getPointOfView() const; 82 | void setPointOfView(const Novel::PointOfView &pointOfView); 83 | 84 | QList plotlines() const; 85 | Plotline *plotline(QUuid id) const; 86 | void setPlotlines(const QList &plotlines); 87 | void addPlotline(Plotline *plotline, int before=-1); 88 | void removePlotline(Plotline *plotline); 89 | void removePlotline(const QUuid id); 90 | 91 | QList characters(); 92 | QList characters(const QRegularExpression &exp) const; 93 | QList characters(const QString &label) const; 94 | QList charactersByName(const QRegularExpression &exp) const; 95 | QList charactersByName(const QString &name) const; 96 | void setCharacters(const QList &characters); 97 | Character *character(const QUuid &id) const; 98 | Character *characterByLabel(const QString label) const; 99 | void addCharacter(Character *character, const int i=-1); 100 | 101 | QJsonObject serialize() const; 102 | static Novel *deserialize(const QJsonObject &object); 103 | QString writeTo(const QString &filePath); 104 | static Novel *readFrom(const QString &filePath, QString *error = 0); 105 | 106 | QStringList revisions() const; 107 | void addRevision(const QString &comment = QString()); 108 | void addRevisions(const QStringList &comments); 109 | void removeRevision(const int index=-1); 110 | void setRevisions(const QStringList &revisions); 111 | void setRevisionComment(const int index, const QString &comment); 112 | void setRevisionComment(const QString &comment); 113 | QString revisionComment(const int index=-1) const; 114 | int revisionCount() const; 115 | void goToLatestRevision(); 116 | 117 | int currentRevision() const; 118 | void setCurrentRevision(int currentRevison); 119 | 120 | Author *author() const; 121 | void setAuthor(Author *author); 122 | 123 | private: 124 | 125 | static const QString JSON_WORKING_TITLE, 126 | JSON_GENRE, 127 | JSON_SETTING, 128 | JSON_TENSE, 129 | JSON_POV, 130 | JSON_SCENES, 131 | JSON_AUTHOR, 132 | JSON_CHARACTERS, 133 | JSON_CHAPTERS, 134 | JSON_PLOTLINES, 135 | JSON_REVISIONS, 136 | JSON_CURRENT_REVISION; 137 | 138 | QString mWorkingTitle, 139 | mGenre, 140 | mSetting; 141 | Novel::Tense mTense; 142 | Novel::PointOfView mPointOfView; 143 | Author *mAuthor; 144 | QList mCharacters; 145 | QList mScenes; 146 | QList mChapters; 147 | QList mPlotlines; 148 | 149 | QStringList mRevisions; 150 | int mCurrentRevision; 151 | 152 | signals: 153 | 154 | void plotlinesChanged(); 155 | 156 | public slots: 157 | 158 | }; 159 | 160 | #endif // NOVEL_H 161 | -------------------------------------------------------------------------------- /src/lib/plotline.cpp: -------------------------------------------------------------------------------- 1 | #include "plotline.h" 2 | 3 | const QString Plotline::J_CHARACTERS = "characters", 4 | Plotline::J_SCENES = "scenes", 5 | Plotline::J_SYNOPSIS = "synopsis", 6 | Plotline::J_BRIEF = "brief", 7 | Plotline::J_COLOR = "color"; 8 | 9 | Plotline::Plotline(const QString &brief, 10 | const QString &synopsis, 11 | const QList &scenes, 12 | const QList &characters, 13 | const QColor &color, 14 | Novel *novel, 15 | QUuid id, 16 | QObject *parent) 17 | : QObject(parent), Serializable(id) 18 | { 19 | this->mBrief = brief; 20 | this->mSynopsis = synopsis; 21 | this->mColor = color; 22 | this->mScenes = scenes; 23 | this->mNovel = novel; 24 | this->mCharacters = characters; 25 | } 26 | 27 | 28 | QString Plotline::brief() const 29 | { 30 | return mBrief; 31 | } 32 | 33 | void Plotline::setBrief(const QString &brief) 34 | { 35 | mBrief = brief; 36 | } 37 | 38 | QColor Plotline::getColor() const 39 | { 40 | return mColor; 41 | } 42 | 43 | void Plotline::setColor(const QColor &color) 44 | { 45 | this->mColor = color; 46 | } 47 | 48 | QString Plotline::synopsis() const 49 | { 50 | return mSynopsis; 51 | } 52 | 53 | void Plotline::setSynopsis(const QString &synopsis) 54 | { 55 | this->mSynopsis = synopsis; 56 | } 57 | 58 | QList Plotline::characters() const 59 | { 60 | return mCharacters; 61 | } 62 | 63 | void Plotline::setCharacters(const QList &characters) 64 | { 65 | this->mCharacters = characters; 66 | } 67 | 68 | void Plotline::addCharacter(Character *character, const int before) 69 | { 70 | if (before > 0) 71 | mCharacters.insert(before, character); 72 | else 73 | mCharacters.append(character); 74 | } 75 | 76 | void Plotline::removeCharacter(Character *character) 77 | { 78 | this->mCharacters.removeAll(character); 79 | } 80 | 81 | void Plotline::removeCharacter(const QString &label) 82 | { 83 | for (Character *c : mCharacters) 84 | if (c->label() == label) 85 | mCharacters.removeAll(c); 86 | } 87 | 88 | QList Plotline::getScenes() const 89 | { 90 | return this->mScenes; 91 | } 92 | 93 | void Plotline::setScenes(const QList &scenes) 94 | { 95 | mScenes = scenes; 96 | } 97 | 98 | Novel *Plotline::novel() 99 | { 100 | return this->mNovel; 101 | } 102 | 103 | void Plotline::setNovel(Novel *novel) 104 | { 105 | this->mNovel = novel; 106 | } 107 | 108 | QJsonObject Plotline::serialize() const{ 109 | QJsonObject plotline = QJsonObject(); 110 | 111 | QJsonArray jCharacters = QJsonArray(); 112 | for (Character *c : mCharacters) 113 | jCharacters.append(c->id().toString()); 114 | 115 | QJsonArray jScenes = QJsonArray(); 116 | for (Scene *s : mScenes) 117 | jScenes.append(s->id().toString()); 118 | 119 | plotline[J_CHARACTERS] = jCharacters; 120 | plotline[J_SCENES] = jScenes; 121 | plotline[J_BRIEF] = mBrief; 122 | plotline[J_SYNOPSIS] = mSynopsis; 123 | plotline[J_COLOR] = mColor.name(); 124 | plotline[JSON_ID] = id().toString(); 125 | 126 | return plotline; 127 | } 128 | 129 | Plotline *Plotline::deserialize(Novel *novel, const QJsonObject &object) 130 | { 131 | QUuid id = Serializable::deserialize(object); 132 | 133 | QString brief = QString(), synopsis = QString(); 134 | QList characters; 135 | QList scenes; 136 | QColor color = QColor(); 137 | 138 | if (object.contains(J_BRIEF)) 139 | brief = object[J_BRIEF].toString(); 140 | 141 | if (object.contains(J_SYNOPSIS)) 142 | synopsis = object[J_SYNOPSIS].toString(); 143 | 144 | if (object.contains(J_CHARACTERS)) 145 | for (QJsonValue val : object[J_CHARACTERS].toArray()) 146 | characters.append(novel->character(QUuid(val.toString()))); 147 | 148 | if (object.contains(J_SCENES)) 149 | for (QJsonValue val : object[J_SCENES].toArray()) 150 | scenes.append(novel->scene(QUuid(val.toString()))); 151 | 152 | if (object.contains(J_COLOR)) 153 | color = QColor(object[J_COLOR].toString()); 154 | 155 | Plotline *plotline = new Plotline(brief, synopsis, scenes, characters, 156 | color, novel, id); 157 | 158 | return plotline; 159 | } 160 | 161 | QList Plotline::deserialize(Novel *novel, const QJsonArray &object) 162 | { 163 | 164 | QList plotlines = QList(); 165 | 166 | for (QJsonValue value : object) 167 | if (value.isObject()) 168 | plotlines.append(Plotline::deserialize(novel, value.toObject())); 169 | 170 | return plotlines; 171 | } 172 | -------------------------------------------------------------------------------- /src/lib/plotline.h: -------------------------------------------------------------------------------- 1 | #ifndef PLOTLINE_H 2 | #define PLOTLINE_H 3 | 4 | #include 5 | #include 6 | #include "character.h" 7 | #include "scene.h" 8 | #include "serializable.h" 9 | 10 | class Novel; 11 | class Character; 12 | class Scene; 13 | 14 | class Plotline : public QObject, public Serializable 15 | { 16 | Q_OBJECT 17 | 18 | private: 19 | 20 | static const QString J_CHARACTERS, 21 | J_SCENES, 22 | J_SYNOPSIS, 23 | J_BRIEF, 24 | J_COLOR; 25 | 26 | QList mCharacters; 27 | QList mScenes; 28 | QString mBrief; 29 | QString mSynopsis; 30 | QColor mColor; 31 | Novel *mNovel; 32 | 33 | public: 34 | explicit Plotline(const QString &brief, 35 | const QString &synopsis, 36 | const QList &scenes = QList(), 37 | const QList &characters = QList(), 38 | const QColor &color = QColor(), 39 | Novel *novel = 0, 40 | QUuid id = QUuid(), 41 | QObject *parent = 0); 42 | 43 | QString synopsis() const; 44 | void setSynopsis(const QString &value); 45 | 46 | QColor getColor() const; 47 | void setColor(const QColor &value); 48 | 49 | QList characters() const; 50 | void setCharacters(const QList &characters); 51 | void addCharacter(Character *character, const int before = -1); 52 | void removeCharacter(Character *character); 53 | void removeCharacter(const QString &label); 54 | 55 | QList getScenes() const; 56 | void setScenes(const QList &scenes); 57 | 58 | Novel *novel(); 59 | void setNovel(Novel *novel); 60 | 61 | QJsonObject serialize() const; 62 | static Plotline *deserialize(Novel *novel, const QJsonObject &object); 63 | static QList deserialize(Novel *novel, const QJsonArray &object); 64 | 65 | QString brief() const; 66 | void setBrief(const QString &brief); 67 | 68 | signals: 69 | 70 | public slots: 71 | }; 72 | 73 | #endif // PLOTLINE_H 74 | -------------------------------------------------------------------------------- /src/lib/revision.cpp: -------------------------------------------------------------------------------- 1 | #include "revision.h" 2 | 3 | const QString Revision::J_CONTENT = "content", 4 | Revision::J_CHAPTER = "chapter", 5 | Revision::J_IS_COMPLETE = "isComplete"; 6 | 7 | Revision::Revision(const QString &content, Chapter *chapter, bool isComplete, 8 | const QUuid &id, QObject *parent) 9 | : QObject(parent), Serializable(id) 10 | { 11 | mContent = content; 12 | mChapter = chapter; 13 | mIsComplete = isComplete; 14 | } 15 | 16 | QString Revision::content() const 17 | { 18 | return mContent; 19 | } 20 | 21 | void Revision::setContent(const QString &content) 22 | { 23 | mContent = content; 24 | } 25 | 26 | QJsonObject Revision::serialize() const 27 | { 28 | const QUuid id = this->id(); 29 | 30 | QJsonObject object = QJsonObject(); 31 | object[JSON_ID] = id.toString(); 32 | object[J_CONTENT] = mContent; 33 | object[J_IS_COMPLETE] = QJsonValue(mIsComplete); 34 | 35 | return object; 36 | } 37 | 38 | Revision *Revision::deserialize(Novel *novel, Chapter *chapter, 39 | const QJsonObject &object) 40 | { 41 | Q_UNUSED(novel); 42 | QString content = QString(); 43 | bool isComplete = false; 44 | QUuid id; 45 | if (object.contains(J_CONTENT)) 46 | content = object[J_CONTENT].toString(); 47 | if (object.contains(J_IS_COMPLETE)) 48 | isComplete = object[J_IS_COMPLETE].toBool(); 49 | if (object.contains(JSON_ID)) 50 | id = QUuid(object[JSON_ID].toString()); 51 | Revision *revision = new Revision(content, chapter, isComplete, id); 52 | 53 | revision->setChapter(chapter); 54 | 55 | return revision; 56 | } 57 | 58 | QList Revision::deserialize(Novel *novel, Chapter *chapter, 59 | const QJsonArray &array) 60 | { 61 | QList revisions = QList(); 62 | for (QJsonValue obj : array) 63 | revisions << Revision::deserialize(novel, chapter, obj.toObject()); 64 | 65 | return revisions; 66 | } 67 | 68 | bool Revision::isComplete() const 69 | { 70 | return mIsComplete; 71 | } 72 | 73 | void Revision::setIsComplete(bool isComplete) 74 | { 75 | mIsComplete = isComplete; 76 | } 77 | 78 | void Revision::toggleComplete() 79 | { 80 | setIsComplete(!mIsComplete); 81 | } 82 | 83 | Chapter *Revision::chapter() const 84 | { 85 | return mChapter; 86 | } 87 | 88 | void Revision::setChapter(Chapter *chapter) 89 | { 90 | mChapter = chapter; 91 | } 92 | -------------------------------------------------------------------------------- /src/lib/revision.h: -------------------------------------------------------------------------------- 1 | #ifndef REVISION_H 2 | #define REVISION_H 3 | 4 | #include "novel.h" 5 | #include "serializable.h" 6 | #include "completable.h" 7 | #include "chapter.h" 8 | 9 | class Chapter; 10 | class Novel; 11 | 12 | class Revision : public QObject, public Serializable 13 | { 14 | Q_OBJECT 15 | public: 16 | explicit Revision(const QString &content = QString(), 17 | Chapter *chapter = 0, 18 | bool isComplete = false, 19 | const QUuid &id = QUuid(), 20 | QObject *parent = 0); 21 | 22 | QString content() const; 23 | Chapter *chapter() const; 24 | 25 | void setChapter(Chapter *chapter); 26 | void setContent(const QString &content); 27 | 28 | QJsonObject serialize() const; 29 | static Revision *deserialize(Novel *novel, Chapter *chapter, 30 | const QJsonObject &object); 31 | static QList deserialize(Novel *novel, Chapter *chapter, 32 | const QJsonArray &object); 33 | 34 | bool isComplete() const; 35 | void setIsComplete(bool isComplete); 36 | void toggleComplete(); 37 | 38 | private: 39 | QString mContent; 40 | Chapter *mChapter; 41 | bool mIsComplete; 42 | 43 | static const QString J_CONTENT, 44 | J_CHAPTER, 45 | J_IS_COMPLETE; 46 | 47 | signals: 48 | 49 | public slots: 50 | }; 51 | 52 | #endif // REVISION_H 53 | -------------------------------------------------------------------------------- /src/lib/scene.cpp: -------------------------------------------------------------------------------- 1 | #include "scene.h" 2 | 3 | const QString Scene::JSON_HEADLINE = QString("headline"), 4 | Scene::JSON_ACTION = QString("action"), 5 | Scene::JSON_CHARACTERS = QString("characters"), 6 | Scene::JSON_POV_CHARACTERS = QString("povCharacters"), 7 | Scene::JSON_PLOTLINE = QString("plotline"); 8 | 9 | Novel *Scene::getNovel() const 10 | { 11 | return mNovel; 12 | } 13 | 14 | void Scene::setNovel(Novel *novel) 15 | { 16 | mNovel = novel; 17 | } 18 | 19 | Plotline *Scene::plotline() const 20 | { 21 | return mPlotline; 22 | } 23 | 24 | void Scene::setPlotline(Plotline *plotline) 25 | { 26 | mPlotline = plotline; 27 | } 28 | 29 | Scene::Scene(QObject *parent) : Completable(parent), Serializable() 30 | { 31 | 32 | } 33 | 34 | Scene::Scene(const QString &headline, const QString &action, Novel *novel, 35 | Plotline *plotline, const QUuid &id, QObject *parent) : Completable(parent), 36 | Serializable(id) 37 | { 38 | this->mHeadline = headline; 39 | this->mAction = action; 40 | this->mNovel = novel; 41 | mPlotline = plotline; 42 | } 43 | 44 | QString Scene::headline() const 45 | { 46 | return mHeadline; 47 | } 48 | 49 | void Scene::setHeadline(const QString &headline) 50 | { 51 | this->mHeadline = headline; 52 | } 53 | 54 | QString Scene::action() const 55 | { 56 | return mAction; 57 | } 58 | 59 | void Scene::setAction(const QString &action) 60 | { 61 | this->mAction = action; 62 | } 63 | 64 | QList Scene::getCharacters() const 65 | { 66 | return this->mCharacters; 67 | } 68 | 69 | void Scene::setPointsOfView(const QList &characters) 70 | { 71 | this->mPovCharacters = characters; 72 | } 73 | 74 | QList Scene::getPointsOfView() const 75 | { 76 | return this->mPovCharacters; 77 | } 78 | 79 | void Scene::setCharacters(const QList &characters) 80 | { 81 | this->mCharacters = characters; 82 | } 83 | 84 | void Scene::addCharacter(const QUuid &id) 85 | { 86 | mCharacters.append(mNovel->character(id)); 87 | } 88 | 89 | void Scene::removeCharacter(Character *character) 90 | { 91 | mCharacters.removeAll(character); 92 | } 93 | 94 | void Scene::removeCharacter(const QUuid &id) 95 | { 96 | mCharacters.removeAll(mNovel->character(id)); 97 | } 98 | 99 | QJsonObject Scene::serialize() const 100 | { 101 | QJsonObject scene = QJsonObject(); 102 | 103 | // When adding the character list, just add the character 104 | // IDs to avoid redundancy. 105 | 106 | QJsonArray jCharacters = QJsonArray(), 107 | jPovCharacters = QJsonArray(); 108 | 109 | for (Character *c : mCharacters) 110 | jCharacters.append(c->id().toString()); 111 | 112 | for (Character *c : mPovCharacters) 113 | jPovCharacters.append(c->id().toString()); 114 | 115 | scene[JSON_HEADLINE] = mHeadline; 116 | scene[JSON_ACTION] = mAction; 117 | scene[JSON_CHARACTERS] = jCharacters; 118 | scene[JSON_POV_CHARACTERS] = jPovCharacters; 119 | scene[JSON_ID] = id().toString(); 120 | if (mPlotline) 121 | scene.insert(JSON_PLOTLINE, QJsonValue(mPlotline->id().toString())); 122 | 123 | return scene; 124 | } 125 | 126 | Scene *Scene::deserialize(Novel *novel, const QJsonObject &object) 127 | { 128 | 129 | QString headline = QString(), action = QString(); 130 | Plotline *plotline = 0; 131 | 132 | QList characters = QList(), 133 | povCharacters = QList(); 134 | 135 | QStringList missing = QStringList(); 136 | 137 | if (object.contains(JSON_HEADLINE)) 138 | headline = object[JSON_HEADLINE].toString(); 139 | else 140 | missing.append(JSON_HEADLINE); 141 | 142 | if (object.contains(JSON_ACTION)) 143 | action = object[JSON_ACTION].toString(); 144 | else 145 | missing.append(JSON_ACTION); 146 | 147 | if (object.contains(JSON_CHARACTERS)) 148 | for (QJsonValue val : object[JSON_CHARACTERS].toArray()) 149 | characters << novel->character(QUuid(val.toString())); 150 | else 151 | missing.append(JSON_CHARACTERS); 152 | 153 | if (object.contains(JSON_POV_CHARACTERS)) 154 | for (QJsonValue val : object[JSON_POV_CHARACTERS].toArray()) 155 | povCharacters.append(novel->character(QUuid(val.toString()))); 156 | 157 | if (object.contains(JSON_PLOTLINE)) 158 | plotline = novel->plotline(QUuid(object[JSON_PLOTLINE].toString())); 159 | 160 | if (!missing.empty()) 161 | qWarning() << "Scene missing the following fields: " 162 | << missing.join(","); 163 | 164 | QUuid id = QUuid(object[JSON_ID].toString()); 165 | qDebug() << "[+] deserialize scene with id" << id; 166 | Scene *scene = new Scene(headline, action, novel, plotline, id); 167 | scene->setCharacters(characters); 168 | scene->setPointsOfView(povCharacters); 169 | return scene; 170 | } 171 | 172 | QList Scene::deserialize(Novel *novel, const QJsonArray &object) 173 | { 174 | QList scenes; 175 | for (QJsonValue obj : object){ 176 | if (obj.isObject()) 177 | scenes << Scene::deserialize(novel, obj.toObject()); 178 | else 179 | scenes << novel->scene(QUuid(obj.toString())); 180 | } 181 | 182 | return scenes; 183 | } 184 | -------------------------------------------------------------------------------- /src/lib/scene.h: -------------------------------------------------------------------------------- 1 | #ifndef SCENE_H 2 | #define SCENE_H 3 | 4 | #include 5 | #include "completable.h" 6 | #include "character.h" 7 | #include "novel.h" 8 | #include "plotline.h" 9 | 10 | class Novel; 11 | class Character; 12 | class Plotline; 13 | 14 | class Scene : public Completable, public Serializable 15 | { 16 | Q_OBJECT 17 | private: 18 | 19 | QString mHeadline, 20 | mAction; 21 | QList mCharacters; 22 | QList mPovCharacters; 23 | Plotline *mPlotline; 24 | Novel *mNovel; 25 | 26 | static const QString JSON_HEADLINE, 27 | JSON_ACTION, 28 | JSON_CHARACTERS, 29 | JSON_POV_CHARACTERS, 30 | JSON_PLOTLINE; 31 | 32 | public: 33 | explicit Scene(QObject *parent=0); 34 | explicit Scene(const QString &mHeadline, const QString &mAction, 35 | Novel *novel = 0, Plotline *plotline = 0, 36 | const QUuid &id = QUuid(), QObject *parent = 0); 37 | 38 | QString headline() const; 39 | void setHeadline(const QString &mHeadline); 40 | 41 | QString action() const; 42 | void setAction(const QString &value); 43 | 44 | void setCharacters(const QList &mCharacters); 45 | void addCharacter(Character *character); 46 | void addCharacter(const QUuid &id); 47 | void removeCharacter(Character *character); 48 | void removeCharacter(const QUuid &id); 49 | QList getCharacters() const; 50 | 51 | void setPointsOfView(const QList &mCharacters); 52 | QList getPointsOfView() const; 53 | 54 | Novel *getNovel() const; 55 | void setNovel(Novel *novel); 56 | 57 | QJsonObject serialize() const; 58 | static Scene *deserialize(Novel *novel, const QJsonObject &object); 59 | static QList deserialize(Novel *novel, const QJsonArray &object); 60 | 61 | Plotline *plotline() const; 62 | void setPlotline(Plotline *plotline); 63 | 64 | signals: 65 | 66 | public slots: 67 | }; 68 | 69 | #endif // SCENE_H 70 | -------------------------------------------------------------------------------- /src/lib/serializable.cpp: -------------------------------------------------------------------------------- 1 | #include "serializable.h" 2 | 3 | const QString Serializable::JSON_ID = "id"; 4 | 5 | Serializable::Serializable(QUuid id) 6 | { 7 | if (id.isNull()){ 8 | mId = QUuid::createUuid(); 9 | } else { 10 | mId = id; 11 | } 12 | } 13 | 14 | QUuid Serializable::deserialize(const QJsonObject &object) 15 | { 16 | QJsonValue value = object.value(JSON_ID); 17 | if (!value.isNull()) 18 | return QUuid(value.toString()); 19 | return QUuid::createUuid(); 20 | } 21 | 22 | QUuid Serializable::id() const 23 | { 24 | return mId; 25 | } 26 | 27 | void Serializable::setId(const QUuid id) 28 | { 29 | mId = id; 30 | } 31 | 32 | -------------------------------------------------------------------------------- /src/lib/serializable.h: -------------------------------------------------------------------------------- 1 | #ifndef SERIALIZABLE_H 2 | #define SERIALIZABLE_H 3 | 4 | #include 5 | #include 6 | 7 | class Serializable 8 | { 9 | private: 10 | 11 | static int sCurrentId; 12 | // Yes, this is kind of awkard (this will generate a new ID for every 13 | // SERIALIZABLE instance instead of every subclass instance, but this 14 | // will take the work-load off the subclasses. 15 | 16 | QUuid mId; 17 | protected: 18 | 19 | static const QString JSON_ID; 20 | 21 | public: 22 | Serializable(QUuid id = QUuid()); 23 | 24 | virtual QJsonObject serialize() const = 0; 25 | static QUuid deserialize(const QJsonObject &object); 26 | 27 | QUuid id() const; 28 | void setId(const QUuid id); 29 | }; 30 | 31 | #endif // SERIALIZABLE_H 32 | -------------------------------------------------------------------------------- /src/lib/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef UTILS_H 2 | #define UTILS_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | // Thanks to http://stackoverflow.com/a/32378165 14 | 15 | QJsonValue jsonValFromPixmap(const QPixmap &p); 16 | QJsonValue jsonValFromImage(const QImage &i); 17 | QPixmap pixmapFrom(const QJsonValue & val); 18 | QImage imageFrom(const QJsonValue &val); 19 | QString friendlyList(const QStringList &stringList); 20 | 21 | // Thanks to http://stackoverflow.com/a/7077340 22 | void clearLayout(QLayout* layout, bool deleteWidgets = true); 23 | void setEnabledRecursive(QLayout *layout, bool enabled); 24 | void setDisabledRecursive(QLayout *layout, bool enabled); 25 | 26 | int findCharReverse(const QString &chars, const QString &s, const int from, 27 | const QString &escape=QString()); 28 | QString substring(const QString &string, int start, int end=-1); 29 | QPair multilineOffset(QString string, int offset); 30 | QString reflowParagraph(const QString ¶graph, const int width); 31 | QString reflowParagraphs(const QString ¶graph, const int width); 32 | 33 | // Number conversion (intended for chapters) 34 | QString toRomanNumeral(const int number); 35 | QString toWord(const int number); 36 | 37 | #endif // UTILS_H 38 | -------------------------------------------------------------------------------- /src/res/images/.directory: -------------------------------------------------------------------------------- 1 | [Dolphin] 2 | PreviewsShown=true 3 | Timestamp=2016,1,30,9,39,10 4 | Version=3 5 | -------------------------------------------------------------------------------- /src/res/images/ATTRIBUTIONS.txt: -------------------------------------------------------------------------------- 1 | clapboard.svg 2 | https://openclipart.org/detail/84157/video 3 | -------------------------------------------------------------------------------- /src/res/images/application-exit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/src-r-r/plotline/726ab94d379ca21206f6979d2ebb92be83149593/src/res/images/application-exit.png -------------------------------------------------------------------------------- /src/res/images/book.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/src-r-r/plotline/726ab94d379ca21206f6979d2ebb92be83149593/src/res/images/book.png -------------------------------------------------------------------------------- /src/res/images/document-new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/src-r-r/plotline/726ab94d379ca21206f6979d2ebb92be83149593/src/res/images/document-new.png -------------------------------------------------------------------------------- /src/res/images/document-open-folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/src-r-r/plotline/726ab94d379ca21206f6979d2ebb92be83149593/src/res/images/document-open-folder.png -------------------------------------------------------------------------------- /src/res/images/document-open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/src-r-r/plotline/726ab94d379ca21206f6979d2ebb92be83149593/src/res/images/document-open.png -------------------------------------------------------------------------------- /src/res/images/document-save-as.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/src-r-r/plotline/726ab94d379ca21206f6979d2ebb92be83149593/src/res/images/document-save-as.png -------------------------------------------------------------------------------- /src/res/images/document-save.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/src-r-r/plotline/726ab94d379ca21206f6979d2ebb92be83149593/src/res/images/document-save.png -------------------------------------------------------------------------------- /src/res/images/edit-clear-locationbar-rtl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/src-r-r/plotline/726ab94d379ca21206f6979d2ebb92be83149593/src/res/images/edit-clear-locationbar-rtl.png -------------------------------------------------------------------------------- /src/res/images/emblem-favorite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/src-r-r/plotline/726ab94d379ca21206f6979d2ebb92be83149593/src/res/images/emblem-favorite.png -------------------------------------------------------------------------------- /src/res/images/fill-color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/src-r-r/plotline/726ab94d379ca21206f6979d2ebb92be83149593/src/res/images/fill-color.png -------------------------------------------------------------------------------- /src/res/images/format-list-ordered.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/src-r-r/plotline/726ab94d379ca21206f6979d2ebb92be83149593/src/res/images/format-list-ordered.png -------------------------------------------------------------------------------- /src/res/images/list-add-plotline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/src-r-r/plotline/726ab94d379ca21206f6979d2ebb92be83149593/src/res/images/list-add-plotline.png -------------------------------------------------------------------------------- /src/res/images/list-add-user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/src-r-r/plotline/726ab94d379ca21206f6979d2ebb92be83149593/src/res/images/list-add-user.png -------------------------------------------------------------------------------- /src/res/images/list-add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/src-r-r/plotline/726ab94d379ca21206f6979d2ebb92be83149593/src/res/images/list-add.png -------------------------------------------------------------------------------- /src/res/images/list-remove-plotline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/src-r-r/plotline/726ab94d379ca21206f6979d2ebb92be83149593/src/res/images/list-remove-plotline.png -------------------------------------------------------------------------------- /src/res/images/list-remove-user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/src-r-r/plotline/726ab94d379ca21206f6979d2ebb92be83149593/src/res/images/list-remove-user.png -------------------------------------------------------------------------------- /src/res/images/list-remove.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/src-r-r/plotline/726ab94d379ca21206f6979d2ebb92be83149593/src/res/images/list-remove.png -------------------------------------------------------------------------------- /src/res/images/plotline-launcher.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 20 | 22 | 25 | 29 | 33 | 34 | 44 | 45 | 68 | 70 | 71 | 73 | image/svg+xml 74 | 76 | 77 | 78 | 79 | 80 | 85 | 93 | 110 | 116 | 122 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /src/res/images/user-archive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/src-r-r/plotline/726ab94d379ca21206f6979d2ebb92be83149593/src/res/images/user-archive.png -------------------------------------------------------------------------------- /src/res/images/user-identity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/src-r-r/plotline/726ab94d379ca21206f6979d2ebb92be83149593/src/res/images/user-identity.png -------------------------------------------------------------------------------- /src/res/images/user-properties.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/src-r-r/plotline/726ab94d379ca21206f6979d2ebb92be83149593/src/res/images/user-properties.png -------------------------------------------------------------------------------- /src/res/images/utilities-file-archiver.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/src-r-r/plotline/726ab94d379ca21206f6979d2ebb92be83149593/src/res/images/utilities-file-archiver.png -------------------------------------------------------------------------------- /src/res/plotline.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | images/application-exit.png 4 | images/book.png 5 | images/document-new.png 6 | images/document-open.png 7 | images/document-save.png 8 | images/emblem-favorite.png 9 | images/list-add-plotline.png 10 | images/list-add-user.png 11 | images/list-remove-plotline.png 12 | images/list-remove-user.png 13 | images/user-archive.png 14 | images/user-identity.png 15 | images/user-properties.png 16 | images/document-save-as.png 17 | images/edit-clear-locationbar-rtl.png 18 | images/fill-color.png 19 | images/list-add.png 20 | images/list-remove.png 21 | images/utilities-file-archiver.png 22 | images/revision.svg 23 | images/format-list-ordered.png 24 | images/45834457236.svg 25 | images/gtk-edit.svg 26 | images/plotline-launcher.svg 27 | images/document-open-folder.png 28 | 29 | 30 | styles/default.json 31 | styles/default-dark.json 32 | 33 | 34 | -------------------------------------------------------------------------------- /src/res/styles/default-dark.json: -------------------------------------------------------------------------------- 1 | { 2 | "META" : { 3 | "name" : "Default (Dark)", 4 | "comment" : "Default theme with a sinister twist." 5 | }, 6 | "style" : { 7 | "document" : { 8 | "foreground" : "#ffffff", 9 | "background" : "#000000" 10 | }, 11 | "HtmlEscape" : { 12 | "foreground" : "#2715b0", 13 | "bold" : true 14 | }, 15 | "Header" : { 16 | "bold" : true, 17 | "relSize" : 3 18 | }, 19 | "BlockQuote" : { 20 | "italic" : true, 21 | "background" : "#aaaaaa" 22 | }, 23 | "List" : { 24 | 25 | }, 26 | "CodeBlock" : { 27 | 28 | }, 29 | "HorizontalRule" : { 30 | 31 | }, 32 | "InlineLink" : { 33 | 34 | }, 35 | "LinkSource" : { 36 | 37 | }, 38 | "LinkTarget" : { 39 | 40 | }, 41 | "Emphasis" : { 42 | "italic" : true 43 | }, 44 | "Strong" : { 45 | "bold" : true 46 | }, 47 | "InlineCode" : { 48 | 49 | }, 50 | "Image" : { 51 | 52 | }, 53 | "AutomaticLink" : { 54 | 55 | }, 56 | "BackslashEscape" : { 57 | 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/res/styles/default.json: -------------------------------------------------------------------------------- 1 | { 2 | "META" : { 3 | "name" : "Default", 4 | "comment" : "Default plotline theme." 5 | }, 6 | "style" : { 7 | "document" : { 8 | "foreground" : "#000000", 9 | "background" : "#ffffff" 10 | }, 11 | "HtmlEscape" : { 12 | "foreground" : "#2715b0", 13 | "bold" : true 14 | }, 15 | "Header" : { 16 | "bold" : true, 17 | "relSize" : 3 18 | }, 19 | "BlockQuote" : { 20 | "italic" : true, 21 | "background" : "#aaaaaa" 22 | }, 23 | "List" : { 24 | 25 | }, 26 | "CodeBlock" : { 27 | 28 | }, 29 | "HorizontalRule" : { 30 | 31 | }, 32 | "InlineLink" : { 33 | 34 | }, 35 | "LinkSource" : { 36 | 37 | }, 38 | "LinkTarget" : { 39 | 40 | }, 41 | "Emphasis" : { 42 | "italic" : true 43 | }, 44 | "Strong" : { 45 | "bold" : true 46 | }, 47 | "InlineCode" : { 48 | 49 | }, 50 | "Image" : { 51 | 52 | }, 53 | "AutomaticLink" : { 54 | 55 | }, 56 | "BackslashEscape" : { 57 | 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/src.pro: -------------------------------------------------------------------------------- 1 | ###################################################################### 2 | # Automatically generated by qmake (3.0) Tue Jan 19 18:57:44 2016 3 | ###################################################################### 4 | 5 | TEMPLATE = subdirs 6 | 7 | CONFIG += ordered 8 | 9 | SUBDIRS += \ 10 | lib \ 11 | app 12 | 13 | CONFIG += gnu++11 14 | 15 | app.depends = lib 16 | test.depends = lib 17 | -------------------------------------------------------------------------------- /test/test.pro: -------------------------------------------------------------------------------- 1 | ###################################################################### 2 | # Automatically generated by qmake (3.0) Tue Jan 19 19:02:44 2016 3 | ###################################################################### 4 | 5 | TEMPLATE = subdirs 6 | 7 | SUBDIRS += \ 8 | ./unit 9 | 10 | LIBS += -L$$OUT_PWD../../src/lib/libplotline.a 11 | 12 | INCLUDEPATH += \ 13 | $$PWD/../../src/lib 14 | 15 | CONFIG += c++11 16 | -------------------------------------------------------------------------------- /test/unit/fixtures/chapter-deserialize.json: -------------------------------------------------------------------------------- 1 | { 2 | "id" : 11, 3 | "title" : "My First Chapter", 4 | "scenes" : [ 5 | 2, 6 | 3, 7 | 4 8 | ], 9 | "revisions" : [ 10 | { 11 | "id" : 5, 12 | "content" : "Revision 1 content", 13 | "isComplete" : true 14 | }, 15 | 16 | { 17 | "id" : 6, 18 | "content" : "Revision 2 content (was 1)", 19 | "isComplete" : false 20 | } 21 | 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /test/unit/fixtures/character-deserialize.json: -------------------------------------------------------------------------------- 1 | { 2 | "id" : 2, 3 | "name" : "Theodore Roosevelt", 4 | "label" : "ThRo", 5 | "scenes" : [9, 10, 11], 6 | "color" : "#ffaaff" 7 | } 8 | -------------------------------------------------------------------------------- /test/unit/fixtures/novel-deserialize.json: -------------------------------------------------------------------------------- 1 | { 2 | "id" : 30, 3 | "workingTitle" : "My Novel", 4 | "genre" : "My Genre", 5 | "scenes" : [ 6 | { 7 | "id" : 2, 8 | "headline" : "Headline 1", 9 | "action" : "Action 1", 10 | "characters" : [ 5, 6, 7 ], 11 | "povCharacters" : [ 5 ] 12 | }, 13 | { 14 | "id" : 3, 15 | "headline" : "Headline 2", 16 | "action" : "Action 2", 17 | "characters" : [ 6, 7 ], 18 | "povCharacters" : [ 6 ] 19 | }, 20 | { 21 | "id" : 4, 22 | "headline" : "Headline 3", 23 | "action" : "Action 3", 24 | "characters" : [ 5, 7 ], 25 | "povCharacters" : [ 5, 7 ] 26 | } 27 | ], 28 | "characters" : [ 29 | { 30 | "id" : 5, 31 | "name" : "George Washington", 32 | "label" : "GeWa" 33 | }, 34 | { 35 | "id" : 6, 36 | "name" : "Abraham Lincoln", 37 | "label" : "AbLi" 38 | }, 39 | { 40 | "id" : 7, 41 | "name" : "Robert E. Lee", 42 | "label" : "RoELe" 43 | } 44 | ], 45 | "chapters" : [ 46 | { 47 | "id" : 8, 48 | "title" : "Chapter 1", 49 | "scenes" : [ 2 ] 50 | }, 51 | { 52 | "id" : 9, 53 | "title" : "Chapter 2", 54 | "scenes" : [ 3 ] 55 | }, 56 | { 57 | "id" : 10, 58 | "title" : "Chapter 3", 59 | "scenes" : [ 3, 4 ] 60 | } 61 | ] 62 | } 63 | -------------------------------------------------------------------------------- /test/unit/fixtures/revision-bug-test.json: -------------------------------------------------------------------------------- 1 | { 2 | "id" : 1, 3 | "workingTitle" : "Novel Title", 4 | "genre" : "Novel Genre", 5 | "revisions" : [ 6 | "First Revision", 7 | "Second Revision", 8 | "Third Revision" 9 | ], 10 | "chapters" : [ 11 | { 12 | "id" : 2, 13 | "title" : "Chapter 1", 14 | "revisions" : [ 15 | { 16 | "id" : 3, 17 | "isComplete" : true, 18 | "content" : "Chapter One, Revision 0" 19 | }, 20 | { 21 | "id" : 4, 22 | "isComplete" : false, 23 | "content" : "Chapter One, Revision 1" 24 | }, 25 | { 26 | "id" : 5, 27 | "isComplete" : false, 28 | "content" : "Chapter One, Revision 2" 29 | } 30 | ] 31 | }, 32 | { 33 | "id" : 6, 34 | "title" : "Chapter 2", 35 | "revisions" : [ 36 | { 37 | "id" : 7, 38 | "isComplete" : true, 39 | "content" : "Chapter Two, Revision 0" 40 | }, 41 | { 42 | "id" : 8, 43 | "isComplete" : false, 44 | "content" : "Chapter Two, Revision 1" 45 | } 46 | ] 47 | } 48 | ] 49 | } 50 | -------------------------------------------------------------------------------- /test/unit/fixtures/revision-deserialize.json: -------------------------------------------------------------------------------- 1 | { 2 | "id" : 2, 3 | "content" : "Revision Content", 4 | "isComplete" : true 5 | } 6 | -------------------------------------------------------------------------------- /test/unit/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "tst_utils.h" 8 | #include "tst_revision.h" 9 | #include "tst_character.cpp" 10 | #include "tst_chapter.cpp" 11 | #include "tst_novel.cpp" 12 | #include "tst_scene.cpp" 13 | #include "tst_plotline.h" 14 | #include "testcharacterparser.h" 15 | 16 | int main(int argc, char *argv[]) 17 | { 18 | 19 | QObject *tests[] = { 20 | 21 | new TestUtils(), 22 | // new TestCharacter(), 23 | new TestRevision(), 24 | // new TestScene(), 25 | // new TestChapter(), 26 | // new TestNovel(), 27 | // new TestPlotline(), 28 | // new TestCharacterParser(), 29 | 30 | NULL 31 | }; 32 | 33 | QGuiApplication a(argc, argv); 34 | 35 | int nFailed = 0; 36 | 37 | for (int i = 0; tests[i]; ++i) 38 | nFailed += QTest::qExec(tests[i], argc, argv); 39 | 40 | const char *border = "+++++++++++++++++"; 41 | 42 | if (nFailed > 0) 43 | qWarning() << border << nFailed << " tests failed. " 44 | << border; 45 | else 46 | qDebug() << border << "All tests passed" << border; 47 | 48 | } 49 | -------------------------------------------------------------------------------- /test/unit/testcharacterparser.cpp: -------------------------------------------------------------------------------- 1 | #include "testcharacterparser.h" 2 | 3 | TestCharacterParser::TestCharacterParser(QObject *parent) : QObject(parent) 4 | { 5 | 6 | } 7 | 8 | void TestCharacterParser::testParser() 9 | { 10 | Novel *novel = new Novel("Test Novel"); 11 | 12 | Character *c1 = new Character("Bob McCalin"), 13 | *c2 = new Character("Sue Robinson"), 14 | *c3 = new Character("Henry Scott"); 15 | 16 | novel->addCharacter(c1); 17 | novel->addCharacter(c2); 18 | novel->addCharacter(c3); 19 | 20 | QString string = QString("This scene will include @BoMc and @SuRo ") + 21 | QString("but will not include @HeS (Henry Scott) ") + 22 | QString("because someone made a typo."); 23 | 24 | ParsedCharacterSet set = ParsedCharacterSet::parse(novel, string); 25 | 26 | int i1 = string.indexOf("@BoMc"), 27 | i2 = string.indexOf("@SuRo"), 28 | i3 = string.indexOf("@HeS"); 29 | 30 | for (int i : set.keys()) 31 | qDebug() << i << ":" << set.value(i)->label(); 32 | 33 | Q_ASSERT(set.contains(i1)); 34 | Q_ASSERT(set.contains(i2)); 35 | Q_ASSERT(!set.contains(i3)); 36 | 37 | Q_ASSERT(set.value(i1) == c1); 38 | Q_ASSERT(set.value(i2) == c2); 39 | 40 | // An initial bug where the function will only find a handful of the 41 | // ocurrances of characters. Find out what went wrong. 42 | 43 | string = QString("@BoMc and @SuRo and @HeSc and @BoMc and @SuRo"); 44 | 45 | set = ParsedCharacterSet::parse(novel, string); 46 | Q_ASSERT(set.count() == 5); 47 | } 48 | 49 | -------------------------------------------------------------------------------- /test/unit/testcharacterparser.h: -------------------------------------------------------------------------------- 1 | #ifndef TESTCHARACTERPARSER_H 2 | #define TESTCHARACTERPARSER_H 3 | 4 | #include 5 | #include 6 | 7 | #include "characterparser.h" 8 | 9 | class TestCharacterParser : public QObject 10 | { 11 | Q_OBJECT 12 | public: 13 | explicit TestCharacterParser(QObject *parent = 0); 14 | 15 | signals: 16 | 17 | public slots: 18 | 19 | 20 | private Q_SLOTS: 21 | 22 | void testParser(); 23 | }; 24 | 25 | #endif // TESTCHARACTERPARSER_H 26 | -------------------------------------------------------------------------------- /test/unit/tst_chapter.cpp: -------------------------------------------------------------------------------- 1 | #include "tst_chapter.h" 2 | 3 | TestChapter::TestChapter(QObject *parent) : QObject(parent) 4 | { 5 | 6 | } 7 | 8 | void TestChapter::initTestCase(){ 9 | mTestCharacters = new QList(); 10 | 11 | mTestScenes = new QList(); 12 | 13 | } 14 | 15 | void TestChapter::testConstructor() 16 | { 17 | Chapter *chapter = new Chapter(); 18 | 19 | chapter = new Chapter(QString("My First Chapter")); 20 | QTRY_COMPARE(chapter->title(), QString("My First Chapter")); 21 | 22 | // Test it with revisisons. 23 | 24 | delete chapter; 25 | } 26 | 27 | void TestChapter::testSetScenes() 28 | { 29 | Chapter *chapter1 = new Chapter(QString("My First Chapter")); 30 | 31 | Scene *s1 = new Scene( 32 | QString("First test scene."), 33 | QString("This is my first test scene. Are you excited?") 34 | ), 35 | *s2 = new Scene ( 36 | QString("2nd Test Scene"), 37 | QString("This is my second test scene! Now I'm really excited.")), 38 | *s3 = new Scene ( 39 | QString("3rd test scene."), 40 | QString("Okay, enough for now. 3 should be enough.")); 41 | 42 | QList ch1Scenes = QList(); 43 | ch1Scenes.append(s1); 44 | ch1Scenes.append(s2); 45 | ch1Scenes.append(s3); 46 | chapter1->setScenes(ch1Scenes); 47 | 48 | Q_ASSERT(chapter1->scenes().size() == 3); 49 | } 50 | 51 | void TestChapter::testAddRevision() 52 | { 53 | Chapter *c = new Chapter("Chapter"); 54 | Revision *r1 = c->addRevision(); 55 | 56 | Q_ASSERT(c->revisions().length() == 1); 57 | Q_ASSERT(c->revisions()[0]->content().isNull()); 58 | 59 | QString s1 = "Hte quick brown fox jumps over teh lazy dog.", 60 | s2 = "The quick brown fox jumps over the lazy dog."; 61 | 62 | r1->setContent(s1); 63 | Revision *r2 = c->addRevision(); 64 | 65 | Q_ASSERT(c->revisions().length() == 2); 66 | QTRY_COMPARE(c->revisions()[0]->content(), s1); 67 | QTRY_COMPARE(c->revisions()[1]->content(), s1); 68 | 69 | r2->setContent(s2); 70 | QTRY_COMPARE(c->revisions()[1]->content(), s2); 71 | } 72 | 73 | void TestChapter::cleanupTestCase() 74 | { 75 | delete mTestCharacters; 76 | delete mTestScenes; 77 | } 78 | 79 | void TestChapter::testSerialize() 80 | { 81 | Chapter *chapter1 = new Chapter(QString("My First Chapter")); 82 | 83 | Scene *s1 = new Scene( 84 | QString("First test scene."), 85 | QString("This is my first test scene. Are you excited?") 86 | ), 87 | *s2 = new Scene ( 88 | QString("2nd Test Scene"), 89 | QString("This is my second test scene! Now I'm really excited.")), 90 | *s3 = new Scene ( 91 | QString("3rd test scene."), 92 | QString("Okay, enough for now. 3 should be enough.")); 93 | QList ch1Scenes = QList(); 94 | ch1Scenes.append(s1); 95 | ch1Scenes.append(s2); 96 | ch1Scenes.append(s3); 97 | chapter1->setScenes(ch1Scenes); 98 | 99 | QJsonObject jChapter = chapter1->serialize(); 100 | Q_ASSERT(jChapter.contains(QString("title"))); 101 | Q_ASSERT(jChapter.contains(QString("scenes"))); 102 | 103 | QTRY_COMPARE(jChapter["title"].toString(), QString("My First Chapter")); 104 | 105 | QJsonDocument doc = QJsonDocument(jChapter); 106 | } 107 | 108 | void TestChapter::testDeserialize() 109 | { 110 | 111 | Novel *novel = new Novel(QString("My Novel"), QString("My Genre")); 112 | 113 | Scene *s1 = new Scene( 114 | QString("First test scene."), 115 | QString("This is my first test scene. Are you excited?") 116 | ), 117 | *s2 = new Scene ( 118 | QString("2nd Test Scene"), 119 | QString("This is my second test scene! Now I'm really excited.")), 120 | *s3 = new Scene ( 121 | QString("3rd test scene."), 122 | QString("Okay, enough for now. 3 should be enough.")); 123 | s1->setId(QUuid::createUuid()); 124 | s2->setId(QUuid::createUuid()); 125 | s3->setId(QUuid::createUuid()); 126 | QList ch1Scenes = QList(); 127 | ch1Scenes.append(s1); 128 | ch1Scenes.append(s2); 129 | ch1Scenes.append(s3); 130 | novel->setScenes(ch1Scenes); 131 | 132 | QFile *json = new QFile(QString("../../../Plotline/test/unit/fixtures/chapter-deserialize.json")); 133 | Q_ASSERT(json->open(QFile::ReadOnly) == true); 134 | QByteArray jsonContent = json->readAll(); 135 | qDebug() << "Parsing content" << jsonContent; 136 | QJsonParseError *error = new QJsonParseError(); 137 | QJsonDocument doc = QJsonDocument::fromJson(jsonContent, error); 138 | json->close(); 139 | 140 | if (doc.isEmpty() || doc.isNull()){ 141 | qDebug() << error->errorString(); 142 | qFatal("Could not open json file."); 143 | } 144 | 145 | Chapter *chapter = Chapter::deserialize(novel, doc.object()); 146 | 147 | QTRY_COMPARE(chapter->title(), QString("My First Chapter")); 148 | Q_ASSERT(chapter->scenes().size() == 3); 149 | QTRY_COMPARE(chapter->scenes()[0], s1); 150 | QTRY_COMPARE(chapter->scenes()[1], s2); 151 | QTRY_COMPARE(chapter->scenes()[2], s3); 152 | } 153 | -------------------------------------------------------------------------------- /test/unit/tst_chapter.h: -------------------------------------------------------------------------------- 1 | #ifndef TST_CHAPTER_H 2 | #define TST_CHAPTER_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "chapter.h" 11 | 12 | class TestChapter : public QObject 13 | { 14 | Q_OBJECT 15 | 16 | public: 17 | TestChapter(QObject *parent = 0); 18 | 19 | private: 20 | QList *mTestCharacters; 21 | QList *mTestScenes; 22 | 23 | private Q_SLOTS: 24 | void initTestCase(); 25 | void cleanupTestCase(); 26 | 27 | void testConstructor(); 28 | void testSetScenes(); 29 | void testAddRevision(); 30 | void testSerialize(); 31 | void testDeserialize(); 32 | }; 33 | 34 | #endif // TST_CHAPTER_H 35 | 36 | -------------------------------------------------------------------------------- /test/unit/tst_character.cpp: -------------------------------------------------------------------------------- 1 | #include "tst_character.h" 2 | 3 | void TestCharacter::initTestCase() 4 | { 5 | 6 | } 7 | 8 | void TestCharacter::cleanupTestCase() 9 | { 10 | 11 | } 12 | 13 | TestCharacter::TestCharacter(QObject *parent) : QObject(parent) 14 | { 15 | 16 | } 17 | 18 | void TestCharacter::testConstructor() 19 | { 20 | Character *character = new Character( QString("David Caine") ); 21 | 22 | QTRY_COMPARE(character->label(), QString("DaCa")); 23 | delete character; 24 | 25 | character = new Character( QString("Matthew")); 26 | QTRY_COMPARE(character->label(), QString("Ma")); 27 | delete character; 28 | 29 | character = new Character( QString("Galactron of the 4th World Order")); 30 | QTRY_COMPARE(character->label(), QString("GaOfTh4tWoOr")); 31 | 32 | character = new Character( QString("Samuel Ricker"), QString("Sammy"), 33 | QString("SaRk")); 34 | QTRY_COMPARE(character->label(), QString("SaRk")); 35 | } 36 | 37 | void TestCharacter::testSerialize() 38 | { 39 | Novel *novel = new Novel(QString("My Title")); 40 | QList scenes = QList(); 41 | scenes.append(new Scene("Scene 1", "Scene 1 Action")); 42 | scenes[0]->setId(QUuid::createUuid()); 43 | scenes.append(new Scene("Scene 2", "Scene 2 Action")); 44 | scenes[1]->setId(QUuid::createUuid()); 45 | scenes.append(new Scene("Scene 3", "Scene 3 Action")); 46 | scenes[2]->setId(QUuid::createUuid()); 47 | Character *c = new Character(QString("Theodore Roosevelt"), 48 | QString("Teddy"), 49 | QString(), 50 | QImage(), 51 | QColor("#ffaaff"), 52 | false, 53 | novel, 54 | QUuid::createUuid()); 55 | 56 | QJsonObject jCharacter = c->serialize(); 57 | QTRY_COMPARE(jCharacter["name"].toString(), QString("Theodore Roosevelt")); 58 | QTRY_COMPARE(jCharacter["nickname"].toString(), QString("Teddy")); 59 | QTRY_COMPARE(jCharacter["label"].toString(), QString("ThRo")); 60 | QTRY_COMPARE(QColor(jCharacter["color"].toString()), QColor("#ffaaff")); 61 | Q_ASSERT(jCharacter["id"].toInt() == 4); 62 | } 63 | 64 | void TestCharacter::testDeserialize() 65 | { 66 | qDebug() << "Current path:" << QDir::currentPath(); 67 | QFile *json = new QFile(QString("../../../Plotline/test/unit/fixtures/character-deserialize.json")); 68 | Q_ASSERT(json->open(QFile::ReadOnly) == true); 69 | QJsonDocument doc = QJsonDocument::fromJson(json->readAll()); 70 | json->close(); 71 | 72 | QUuid testIds[]{QUuid::createUuid(), QUuid::createUuid(), QUuid::createUuid()}; 73 | 74 | Novel *novel = new Novel(QString("My Novel")); 75 | QList scenes = QList(); 76 | scenes << new Scene(QString("Scene 1"), QString("Action 1")) 77 | << new Scene(QString("Scene 2"), QString("Action 2")) 78 | << new Scene(QString("Scene 3"), QString("Action 3")); 79 | for (int i = 0; i < 3; ++i) 80 | scenes[i]->setId(testIds[i]); 81 | novel->setScenes(scenes); 82 | 83 | QJsonObject jCharacter = doc.object(); 84 | Character *character = Character::deserialize(novel, jCharacter); 85 | 86 | qDebug() << "Character's color: " << character->color().name(); 87 | QTRY_COMPARE(character->name(), QString("Theodore Roosevelt")); 88 | QTRY_COMPARE(character->label(), QString("ThRo")); 89 | QTRY_COMPARE(character->color(), QColor("#ffaaff")); 90 | } 91 | -------------------------------------------------------------------------------- /test/unit/tst_character.h: -------------------------------------------------------------------------------- 1 | #ifndef TST_CHARACTER_H 2 | #define TST_CHARACTER_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "character.h" 11 | #include "novel.h" 12 | 13 | class TestCharacter : public QObject 14 | { 15 | Q_OBJECT 16 | 17 | public: 18 | TestCharacter(QObject *parent = 0); 19 | 20 | private: 21 | 22 | private Q_SLOTS: 23 | void initTestCase(); 24 | void cleanupTestCase(); 25 | 26 | void testConstructor(); 27 | 28 | void testSerialize(); 29 | void testDeserialize(); 30 | }; 31 | 32 | #endif // TST_CHARACTER_H 33 | 34 | -------------------------------------------------------------------------------- /test/unit/tst_novel.h: -------------------------------------------------------------------------------- 1 | #ifndef TST_NOVEL_H 2 | #define TST_NOVEL_H 3 | 4 | #include 5 | #include 6 | #include "novel.h" 7 | 8 | class TestNovel : public QObject 9 | { 10 | Q_OBJECT 11 | 12 | public: 13 | TestNovel(QObject *parent = 0); 14 | 15 | private Q_SLOTS: 16 | void testConstructor(); 17 | void testSetters(); 18 | void testSerialize(); 19 | void testDeserialize(); 20 | void testGetCharacters(); 21 | void testRevisions(); 22 | }; 23 | 24 | #endif // TST_NOVEL_H 25 | 26 | -------------------------------------------------------------------------------- /test/unit/tst_plotline.cpp: -------------------------------------------------------------------------------- 1 | #include "tst_plotline.h" 2 | 3 | TestPlotline::TestPlotline(QObject *parent) : QObject(parent) 4 | { 5 | 6 | } 7 | 8 | void TestPlotline::testSerialize() 9 | { 10 | 11 | } 12 | 13 | void TestPlotline::testDeserialize() 14 | { 15 | 16 | } 17 | 18 | void TestPlotline::testConstructor() 19 | { 20 | QList scenes = QList(); 21 | QList characters = QList(); 22 | scenes << new Scene(QString("Headline 1"), QString("Action 1")) 23 | << new Scene(QString("Headline 2"), QString("Action 2")) 24 | << new Scene(QString("Headline 3"), QString("Action 3")); 25 | 26 | characters << new Character(QString("Theodore Roosevelt")) 27 | << new Character(QString("Abraham Lincoln")) 28 | << new Character(QString("Benjamin Franklin")); 29 | 30 | QColor color = QColor("#ffaabb"); 31 | 32 | QUuid id = QUuid::createUuid(); 33 | Plotline *p = new Plotline(QString("Something happens"), 34 | QString("Synopsis 1"), 35 | scenes, 36 | characters, 37 | color, 38 | 0, 39 | id); 40 | 41 | QTRY_COMPARE(p->synopsis(), QString("Synopsis 1")); 42 | QTRY_COMPARE(p->brief(), QString("Something happens")); 43 | QTRY_COMPARE(p->characters(), characters); 44 | Q_ASSERT(p->id() == id); 45 | QTRY_COMPARE(p->getColor(), QColor("#ffaabb")); 46 | QTRY_COMPARE(p->getScenes(), scenes); 47 | 48 | } 49 | -------------------------------------------------------------------------------- /test/unit/tst_plotline.h: -------------------------------------------------------------------------------- 1 | #ifndef TESTPLOTLINE_H 2 | #define TESTPLOTLINE_H 3 | 4 | #include 5 | #include 6 | #include "plotline.h" 7 | 8 | class TestPlotline : public QObject 9 | { 10 | Q_OBJECT 11 | public: 12 | explicit TestPlotline(QObject *parent = 0); 13 | 14 | signals: 15 | 16 | private Q_SLOTS: 17 | void testConstructor(); 18 | void testSerialize(); 19 | void testDeserialize(); 20 | }; 21 | 22 | #endif // TESTPLOTLINE_H 23 | -------------------------------------------------------------------------------- /test/unit/tst_revision.cpp: -------------------------------------------------------------------------------- 1 | #include "tst_revision.h" 2 | 3 | TestRevision::TestRevision(QObject *parent) : QObject(parent) 4 | { 5 | 6 | } 7 | 8 | void TestRevision::testConstructor() 9 | { 10 | Revision *revision = new Revision("Revision Content"); 11 | QTRY_COMPARE(revision->content(), QString("Revision Content")); 12 | 13 | Chapter *chapter = new Chapter(QString("Chapter")); 14 | 15 | revision = new Revision(QString("Revision Content"), chapter); 16 | QTRY_COMPARE(revision->content(), QString("Revision Content")); 17 | QTRY_COMPARE(revision->chapter(), chapter); 18 | 19 | revision = new Revision(QString("Revision Content"), chapter, 20 | false); 21 | Q_ASSERT(revision->isComplete() == false); 22 | revision->toggleComplete(); 23 | Q_ASSERT(revision->isComplete() == true); 24 | } 25 | 26 | void TestRevision::testSerialize() 27 | { 28 | QUuid id = QUuid::createUuid(); 29 | Revision *revision = new Revision(QString("Revision Content"), 0, 30 | true, id); 31 | QJsonObject obj = revision->serialize(); 32 | Q_ASSERT(obj.contains("content")); 33 | QTRY_COMPARE(obj["content"].toString(), QString("Revision Content")); 34 | Q_ASSERT(!obj.contains("chapter")); 35 | Q_ASSERT(obj["isComplete"].toBool() == true); 36 | Q_ASSERT(QUuid(obj["id"].toString()) == id); 37 | } 38 | 39 | void TestRevision::testDeserialize() 40 | { 41 | QFile *json = new QFile(QString("../../../Plotline/test/unit/fixtures/revision-deserialize.json")); 42 | json->open(QFile::ReadOnly); 43 | QJsonDocument doc = QJsonDocument::fromJson(json->readAll()); 44 | json->close(); 45 | 46 | Novel *novel = new Novel(QString("My Novel")); 47 | Chapter *chapter = new Chapter(); 48 | 49 | Revision *revision = Revision::deserialize(novel, chapter, doc.object()); 50 | QTRY_COMPARE(revision->content(), QString("Revision Content")); 51 | // Q_ASSERT(revision->id() == 2); 52 | Q_ASSERT(revision->isComplete() == true); 53 | } 54 | 55 | void TestRevision::testChapterRevisions() 56 | { 57 | QString cont1 = "This is my first chapter and first revision!", 58 | cont2 = "This is my first chapter, but not my first revision."; 59 | Chapter *chapter = new Chapter(); 60 | chapter->setContent(cont1, 0); 61 | chapter->addRevision(); 62 | QVERIFY(!chapter->content(0).isEmpty()); 63 | QCOMPARE(chapter->content(0), chapter->content(1)); 64 | 65 | chapter->setContent(cont2, 1); 66 | QCOMPARE(chapter->content(1), cont2); 67 | } 68 | 69 | void TestRevision::testNovelDeserialize() 70 | { 71 | Novel *novel = Novel::readFrom("../../../Plotline/test/unit/fixtures/revision-bug-test.json"); 72 | if (!novel) QFAIL("Novel is null!"); 73 | Q_ASSERT(novel->revisions().count() == 3); 74 | for (Chapter *c : novel->chapters()){ 75 | qDebug() << "Assert Chapter" << c->number() << "has 3 revisions."; 76 | Q_ASSERT(c->revisions().count() == 3); 77 | } 78 | 79 | // Verify that ch2r2 is an exact copy of ch2r1 80 | Chapter *ch = novel->chapters()[1]; 81 | QVERIFY(!ch->content(2).isEmpty()); 82 | QCOMPARE(ch->content(2), ch->content(1)); 83 | QCOMPARE(ch->content(2), ch->latestContent()); 84 | 85 | Chapter *ch3 = new Chapter("Chapter Three"); 86 | novel->addChapter(ch3); 87 | Q_ASSERT(ch3->revisions().count() == 3); 88 | } 89 | 90 | -------------------------------------------------------------------------------- /test/unit/tst_revision.h: -------------------------------------------------------------------------------- 1 | #ifndef TESTREVISION_H 2 | #define TESTREVISION_H 3 | 4 | #include 5 | #include 6 | #include "revision.h" 7 | 8 | class TestRevision : public QObject 9 | { 10 | Q_OBJECT 11 | public: 12 | explicit TestRevision(QObject *parent = 0); 13 | 14 | signals: 15 | 16 | private Q_SLOTS: 17 | 18 | void testConstructor(); 19 | void testSerialize(); 20 | void testDeserialize(); 21 | 22 | void testChapterRevisions(); 23 | void testNovelDeserialize(); 24 | 25 | }; 26 | 27 | #endif // TESTREVISION_H 28 | -------------------------------------------------------------------------------- /test/unit/tst_scene.cpp: -------------------------------------------------------------------------------- 1 | #include "tst_scene.h" 2 | 3 | TestScene::TestScene(QObject *parent) : QObject(parent) 4 | { 5 | 6 | } 7 | 8 | void TestScene::testConstructor() 9 | { 10 | } 11 | 12 | -------------------------------------------------------------------------------- /test/unit/tst_scene.h: -------------------------------------------------------------------------------- 1 | #ifndef TST_SCENE_H 2 | #define TST_SCENE_H 3 | 4 | #include 5 | #include 6 | #include "scene.h" 7 | 8 | class TestScene : public QObject 9 | { 10 | Q_OBJECT 11 | 12 | public: 13 | TestScene(QObject *parent = 0); 14 | 15 | private Q_SLOTS: 16 | void testConstructor(); 17 | }; 18 | 19 | #endif // TST_SCENE_H 20 | 21 | -------------------------------------------------------------------------------- /test/unit/tst_utils.cpp: -------------------------------------------------------------------------------- 1 | #include "tst_utils.h" 2 | 3 | TestUtils::TestUtils(QObject *parent) : QObject(parent) 4 | { 5 | 6 | } 7 | 8 | void TestUtils::test_findCharReverse() 9 | { 10 | 11 | QString test = "Can you @find this @tag in a string?"; 12 | int pos = test.indexOf("@tag") + QString("@tag").length()-1; 13 | int expected = test.indexOf("@tag"); 14 | int i = findCharReverse("@#", test, pos, " "); 15 | 16 | qDebug() << "Position:" << pos 17 | << ", expected:" << expected 18 | << ", result:" << i; 19 | Q_ASSERT(i == expected); 20 | 21 | test = "There should be nothing found here."; 22 | pos = 14; 23 | expected = -1; 24 | i = findCharReverse("@#", test, pos, " "); 25 | Q_ASSERT(i == expected); 26 | 27 | test = "This @tag should not be found."; 28 | pos = test.indexOf("not"); 29 | expected = -1; 30 | i = findCharReverse("@#", test, pos, " "); 31 | Q_ASSERT(i == expected); 32 | 33 | } 34 | 35 | void TestUtils::test_reflowParagraph() 36 | { 37 | QString p = "aa aaaaaaa bbbbbbbbbbb c d"; 38 | QString exp = "aa aaaaaaa\nbbbbbbbbbbb\nc \nd"; 39 | QString res = reflowParagraph(p, 10); 40 | 41 | QCOMPARE(res, exp); 42 | 43 | p = "aa aaaa\naaaaaa\nbbbb"; 44 | exp = "aa aaaa\naaaaaa bbbb"; 45 | res = reflowParagraph(p, 10); 46 | QCOMPARE(res, exp); 47 | } 48 | 49 | -------------------------------------------------------------------------------- /test/unit/tst_utils.h: -------------------------------------------------------------------------------- 1 | #ifndef TESTUTILS_H 2 | #define TESTUTILS_H 3 | 4 | #include 5 | #include 6 | #include "utils.h" 7 | 8 | class TestUtils : public QObject 9 | { 10 | Q_OBJECT 11 | public: 12 | explicit TestUtils(QObject *parent = 0); 13 | 14 | signals: 15 | 16 | public slots: 17 | 18 | private Q_SLOTS: 19 | 20 | void test_findCharReverse(); 21 | void test_reflowParagraph(); 22 | 23 | }; 24 | 25 | #endif // TESTUTILS_H 26 | -------------------------------------------------------------------------------- /test/unit/unit.pro: -------------------------------------------------------------------------------- 1 | 2 | TEMPLATE = app 3 | 4 | TARGET = plotline-UnitTest 5 | 6 | QT += core gui webkitwidgets printsupport testlib 7 | 8 | HEADERS += tst_chapter.h \ 9 | tst_character.h \ 10 | tst_novel.h \ 11 | tst_plotline.h \ 12 | tst_scene.h \ 13 | tst_revision.h \ 14 | testcharacterparser.h \ 15 | tst_utils.h 16 | 17 | SOURCES += main.cpp \ 18 | tst_chapter.cpp \ 19 | tst_character.cpp \ 20 | tst_novel.cpp \ 21 | tst_plotline.cpp \ 22 | tst_scene.cpp \ 23 | tst_revision.cpp \ 24 | testcharacterparser.cpp \ 25 | tst_utils.cpp 26 | 27 | DISTFILES += \ 28 | fixtures/character-deserialize.json \ 29 | fixtures/chapter-deserialize.json \ 30 | fixtures/novel-deserialize.json \ 31 | fixtures/revision-deserialize.json \ 32 | fixtures/revision-bug-test.json 33 | 34 | ## 35 | # Depending on whether we're building the libraries statically or dynamically, 36 | # Either the .so will be found or the .a will be found. 37 | # There is probably a more elegant solution. 38 | ## 39 | 40 | CONFIG += console testcase c++11 41 | 42 | win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../../src/lib/release/ -lplotline 43 | else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../../src/lib/debug/ -lplotline 44 | else:unix: LIBS += -L$$OUT_PWD/../../src/lib/ -lplotline 45 | 46 | INCLUDEPATH += $$PWD/../../src/lib 47 | DEPENDPATH += $$PWD/../../src/lib 48 | --------------------------------------------------------------------------------