├── .editorconfig ├── .gitignore ├── .travis.yml ├── AUTHORS.md ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── data ├── com.github.cjfloss.envelope.appdata.xml.in ├── com.github.cjfloss.envelope.desktop.in ├── com.github.cjfloss.envelope.gschema.xml ├── icons │ ├── 128x128 │ │ └── apps │ │ │ ├── com.github.cjfloss.envelope.png │ │ │ └── com.github.cjfloss.envelope.svg │ ├── 16x16 │ │ └── apps │ │ │ ├── com.github.cjfloss.envelope.png │ │ │ ├── com.github.cjfloss.envelope.svg │ │ │ └── com.github.cjfloss.envelope.svg.2019_08_04_22_31_24.0.svg │ ├── 24x24 │ │ └── apps │ │ │ ├── com.github.cjfloss.envelope.png │ │ │ └── com.github.cjfloss.envelope.svg │ ├── 32x32 │ │ └── apps │ │ │ ├── com.github.cjfloss.envelope.png │ │ │ ├── com.github.cjfloss.envelope.svg │ │ │ └── com.github.cjfloss.envelope.svg.2019_08_04_22_27_11.0.svg │ ├── 48x48 │ │ └── apps │ │ │ ├── com.github.cjfloss.envelope.png │ │ │ └── com.github.cjfloss.envelope.svg │ └── 64x64 │ │ └── apps │ │ ├── com.github.cjfloss.envelope.png │ │ └── com.github.cjfloss.envelope.svg ├── meson.build ├── post_install.py └── screenshots │ ├── 01.png │ ├── 01@2.png │ ├── 02.png │ ├── 02@2.png │ ├── 03.png │ ├── 03@2.png │ ├── 04.png │ ├── 04@2.png │ ├── 05.png │ ├── 05@2.png │ ├── 06.png │ └── 06@2.png ├── meson.build ├── po ├── LINGUAS ├── POTFILES ├── com.github.cjfloss.envelope.pot ├── de.po ├── extra │ ├── LINGUAS │ ├── POTFILES │ ├── de.po │ ├── extra.pot │ ├── fr.po │ ├── lt.po │ ├── meson.build │ ├── nl.po │ └── pl.po ├── fr.po ├── lt.po ├── meson.build ├── nl.po ├── pl.po └── ru_RU.po ├── src ├── Build.vala ├── Envelope.vala ├── database │ ├── Database.vala │ ├── DatabaseCursor.vala │ ├── DatabaseError.vala │ ├── DatabaseManager.vala │ └── SQLQueries.vala ├── dialog │ ├── AddAccountDialog.vala │ ├── AddCategoryDialog.vala │ └── ImportTransactionsDialog.vala ├── meson.build ├── model │ ├── Account.vala │ ├── Budget.vala │ ├── Category.vala │ ├── MonthlyCategory.vala │ ├── Payee.vala │ └── Transaction.vala ├── service │ ├── AccountManager.vala │ ├── BudgetManager.vala │ ├── CategoryStore.vala │ ├── Error.vala │ ├── Importer.vala │ ├── PayeeStore.vala │ ├── QIFImporter.vala │ └── settings │ │ └── SavedState.vala ├── util │ ├── Date.vala │ └── String.vala ├── vapi │ └── monetary.vapi ├── view │ ├── AccountWelcomeScreen.vala │ ├── BudgetOverview.vala │ ├── CategoryProperties.vala │ ├── FilterView.vala │ ├── Sidebar.vala │ ├── TransactionView.vala │ └── WelcomeScreen.vala ├── widget │ ├── AbstractPopoverCellRenderer.vala │ ├── CellRendererCategoryPicker.vala │ ├── CellRendererDatePicker.vala │ ├── CellRendererPopoverContainer.vala │ ├── CellRendererTextCompletion.vala │ └── CellRendererUpdatable.vala └── window │ └── MainWindow.vala └── tests ├── QIFImporterTest.vala ├── meson.build └── sample.qif /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | tab_width = 4 7 | indent_size = 4 8 | indent_style = space 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | max_line_length = 80 12 | 13 | [{*.html,*.xml,*.xml.in,*.yml}] 14 | indent_size = 2 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | data/icons/Trials 3 | .changelogrc 4 | template.md 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | language: node_js 4 | 5 | node_js: 6 | - 10/* 7 | 8 | sudo: required 9 | 10 | services: 11 | - docker 12 | 13 | addons: 14 | apt: 15 | sources: 16 | - ubuntu-toolchain-r-test 17 | packages: 18 | - libstdc++-5-dev 19 | 20 | matrix: 21 | include: 22 | - env: DIST=loki 23 | - env: DIST=juno 24 | 25 | install: 26 | - npm i -g @elementaryos/houston 27 | 28 | script: 29 | - houston ci 30 | --distribution $DIST 31 | -------------------------------------------------------------------------------- /AUTHORS.md: -------------------------------------------------------------------------------- 1 | # Authors 2 | 3 | * Nicolas Laplante 4 | 5 | # Maintainers: 6 | 7 | * Cleiton Floss 8 | 9 | # Designers: 10 | 11 | * Nicolas Laplante 12 | 13 | # Contributors: 14 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # (2018-01-18) 2 | 3 | ## Bug Fixes 4 | 5 | - **WelcomeScreen** remove junk code 6 | ([b016cb0](https://github.com/cjfloss/envelope/commit/b016cb04c1497f791eedc3347698ab0f76ab1b5d)) 7 | - **MainWindow** 8 | - use overlay_bar.label not status 9 | ([c987842](https://github.com/cjfloss/envelope/commit/c9878423ef75f0d22665fb4d5f35e6adebf2d33e) 10 | - Use Ellipsis instead of three dots 11 | ([1021ef7](https://github.com/cjfloss/envelope/commit/1021ef7dbca43b8f13605bce4fa7abbc750fbe9a)) 12 | - **AbstractPopoverCellRenderer** Remove determine position method 13 | ([29ff103](https://github.com/cjfloss/envelope/commit/29ff1039816e8753f3ee571f79b3805f398b4d58)) 14 | - **Widgets** Add suitable method to override 15 | ([ae06550](https://github.com/cjfloss/envelope/commit/ae06550b24cb8734bd2f34af82a46bf5d0ea53ee)) 16 | - **CategoryProperties** Add try-catch to Handle error 17 | ([46c3d76](https://github.com/cjfloss/envelope/commit/46c3d76a9f315747c7d1929610ff0a3eabee464e)) 18 | - **TransactionView** Convert Title to string 19 | ([92bcf43](https://github.com/cjfloss/envelope/commit/92bcf432ffef65eed1bf1620d290b7441efddb7b)) 20 | - **Granite.TextStyle** Replace deprecated usage modes 21 | ([fef4db4](https://github.com/cjfloss/envelope/commit/fef4db4352ba45bcc37a4e4075cf6d80eb4b042f)) 22 | - **Compilation Warnings** 23 | - Use new format indicate deprecated functions 24 | ([dcfe4e5](https://github.com/cjfloss/envelope/commit/dcfe4e55ae3cd4c508246d1f1148e5125e45322c)) 25 | - Remove modifier 'static' from constants 26 | ([9bd1228](https://github.com/cjfloss/envelope/commit/9bd122814a570fd0768aa0745e5f327d0acd53cc)) 27 | 28 | ## Features 29 | 30 | - **il8n** add internationalization files 31 | ([402ecad](https://github.com/cjfloss/envelope/commit/402ecad58ae3e58520f024fbc2caa109c884e35e)) 32 | - **Desktop File** 33 | - Update Gettext domain 34 | ([46d3015](https://github.com/cjfloss/envelope/commit/46d3015097653406b7a4a7428931cfaac48a19b5)) 35 | - Remove Desktop Action 36 | ([bc6fb4d](https://github.com/cjfloss/envelope/commit/bc6fb4d909e18f3d81bcd4cc929639ca475b9222)) 37 | - Update executable 38 | ([439bbab](https://github.com/cjfloss/envelope/commit/439bbab0c3975ff7ef0a5cfdcee7af1c30ed004a)) 39 | 40 | 41 | # (2015-02-22) 42 | 43 | ## Bug Fixes 44 | 45 | - **Database:** map categories using name instead of id 46 | ([97c1a25b](https://github.com/nlaplante/envelope/commit/97c1a25b6444b836fcadf049856db8810d4150df), 47 | [#40](https://github.com/nlaplante/envelope/issues/40)) 48 | - **Header bar:** remove export button 49 | ([431dc7a6](https://github.com/nlaplante/envelope/commit/431dc7a6986586450e7b9ca503f4e9abd194a949), 50 | [#43](https://github.com/nlaplante/envelope/issues/43)) 51 | 52 | # (2015-02-18) 53 | 54 | ## Bug Fixes 55 | 56 | - **Application name:** Update .desktop name to Envelope 57 | ([f0f0ff39](https://github.com/nlaplante/envelope/commit/f0f0ff39578343bbc60696c4eef38d8b24b4f7d3), 58 | [#39](https://github.com/nlaplante/envelope/issues/39)) 59 | - **TransactionView:** 60 | - remove currency from cell after editing amount 61 | ([8d4530e2](https://github.com/nlaplante/envelope/commit/8d4530e209ff868cceef5c32c5d97c3c08eaf144), 62 | [#41](https://github.com/nlaplante/envelope/issues/41)) 63 | - **Search:** 64 | - change placeholder text when selecting category 65 | ([0c423a3c](https://github.com/nlaplante/envelope/commit/0c423a3cf7f4718ecdfa13eb69a67ef17b50f1e2), 66 | [#19](https://github.com/nlaplante/envelope/issues/19)) 67 | 68 | ## Features 69 | 70 | - **Search:** 71 | - remove ellipsis in placeholder text 72 | ([983fd617](https://github.com/nlaplante/envelope/commit/983fd6179cfb14b49abf4dc056fc36c79cd1d2c2)) 73 | - make search bar wider 74 | ([a766150f](https://github.com/nlaplante/envelope/commit/a766150fce823d96b1e17bc36cf60231eb719eae)) 75 | 76 | # (2015-02-08) 77 | 78 | ## Features 79 | 80 | - **TransactionView:** refilter view 81 | ([c9ea0b23](https://github.com/nlaplante/envelope/commit/c9ea0b23ab777f62a88e84ee9ec84e0c2f394447)) 82 | 83 | ## Bug Fixes 84 | 85 | - **TransactionView:** use correct iter when column is sorted 86 | ([7798e063](https://github.com/nlaplante/envelope/commit/7798e063dc461b323fc738290ce7e073b3c2c982), 87 | [#30](https://github.com/nlaplante/envelope/issues/30)) 88 | 89 | # (2015-02-07) 90 | 91 | ## Features 92 | 93 | - **BudgetManager:** handle null category for uncategorized transactions 94 | ([86ac9749](https://github.com/nlaplante/envelope/commit/86ac9749af33beda476cbafaf50e456ef62e7f32)) 95 | - **Database:** add query for current uncategorized transactions 96 | ([49a03a8a](https://github.com/nlaplante/envelope/commit/49a03a8a4b929012c84f5d8b301900c7c392b8d9)) 97 | - **Settings:** handle uncategorized sidebar selected item 98 | ([641b71e8](https://github.com/nlaplante/envelope/commit/641b71e8c2a794f8b4a3a3c4df42251c0db3c26a)) 99 | - **Sidebar:** uncategorized item shows current uncategorized transactions 100 | ([dbcc71ab](https://github.com/nlaplante/envelope/commit/dbcc71ab21b31720c374fca7af5353823fb47a6b)) 101 | 102 | # (2015-02-04) 103 | 104 | ## Features 105 | 106 | - **MainWindow:** remove Gtk.Revealer 107 | ([89b0a675](https://github.com/nlaplante/envelope/commit/89b0a675a5489332199bd66de6d47c14d5ed945c)) 108 | - **TransactionView:** 109 | - set sane minimum column widths 110 | ([dc983a59](https://github.com/nlaplante/envelope/commit/dc983a594df6edc15530a8a500bfd98e071635d8)) 111 | - ellipsize content when resizing columns 112 | ([116cf16d](https://github.com/nlaplante/envelope/commit/116cf16d8e93b6384b248d79b7f5ba796cf0a825)) 113 | - use fixed row height (perf) 114 | ([3b0eb11f](https://github.com/nlaplante/envelope/commit/3b0eb11fc9c3929a7c0e4ffc0ee4b7ce1d6b57aa)) 115 | 116 | 117 | ## Documentation 118 | 119 | - **README:** add contributing section 120 | 121 | 122 | 123 | --- 124 | *Generated with [git-changelog](https://github.com/rafinskipg/git-changelog). If you have any problem or suggestion, create an issue.* :) **Thanks** 125 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Envelope [![Build Status](https://travis-ci.com/cjfloss/envelope.svg?branch=master)](https://travis-ci.com/cjfloss/envelope) 2 | ### Personal budget manager 3 | ![Screenshot](https://github.com/cjfloss/envelope/raw/master/data/screenshots/03.png) 4 | Envelope helps you maintain your personal budget by using the tried-and-true [envelope system](https://en.wikipedia.org/wiki/Envelope_system). 5 | Designate spending categories (envelopes) and distribute your monthly income into them. 6 | Configure accounts where you record all your transactions, then assign each of them to a category. 7 | 8 | * Envelope system budget workflow 9 | * Import transactions from QIF files 10 | 11 | ## Installation 12 | [![Get it on AppCenter](https://appcenter.elementary.io/badge.svg)](https://appcenter.elementary.io/com.github.cjfloss.envelope) 13 | 14 | ### Dependencies 15 | These dependencies must be present before building 16 | - `meson` 17 | - `ninja-build` 18 | - `Vala` 19 | - `GLib` 20 | - `gio-2.0` 21 | - `GTK` 22 | - `libgee-0.8` 23 | - `Granite` 24 | - `SQlite3` 25 | 26 | **You can install these on a Ubuntu-based system by executing this command:** 27 | 28 | `sudo apt install meson ninja-build valac libgtk-3-dev libgee-0.8-dev libgranite-dev libsqlite3-dev ` 29 | 30 | ### Building 31 | ``` 32 | $ git clone https://github.com/cjfloss/envelope.git 33 | $ meson envelope build 34 | $ ninja -C build 35 | ``` 36 | 37 | ### Installing & executing 38 | ``` 39 | $ sudo ninja -C build install 40 | $ com.github.cjfloss.envelope 41 | ``` 42 | 43 | ## Contributing 44 | 45 | Want to help? Just fork this repository, pick an issue and start hacking. Just follow the [elementary coding style](https://elementary.io/docs/code/reference#code-style) and document your changes. 46 | 47 | ### Commit messages 48 | 49 | Commit messages should follow the [AngularJS commit message conventions](https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit) 50 | -------------------------------------------------------------------------------- /data/com.github.cjfloss.envelope.appdata.xml.in: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | com.github.cjfloss.envelope 5 | CC0 6 | GPL-3.0+ 7 | Envelope 8 | Personal budget manager 9 | com.github.cjfloss.envelope 10 | 11 | 12 | none 13 | none 14 | none 15 | none 16 | none 17 | none 18 | none 19 | none 20 | none 21 | none 22 | none 23 | none 24 | none 25 | none 26 | none 27 | none 28 | none 29 | none 30 | none 31 | none 32 | none 33 | none 34 | none 35 | none 36 | none 37 | none 38 | none 39 | 40 | 41 | 42 |

Maintain your personal budget by using the tried-and-true envelope system. 43 | Designate spending categories (envelopes) and distribute your monthly income into them. 44 | Configure accounts where you record all your transactions, then assign each of them to a category.

45 |

Originally made by Nicolas Laplante.

46 |

Features:

47 |
    48 |
  • Envelope system budget workflow
  • 49 |
  • Import transactions from QIF files
  • 50 |
51 |
52 | 53 | com.github.cjfloss.envelope.desktop 54 | 55 | 56 | com.github.cjfloss.envelope 57 | 58 | 59 | 60 | 61 | Budget Overview 62 | https://github.com/cjfloss/envelope/raw/master/data/screenshots/03.png 63 | https://github.com/cjfloss/envelope/raw/master/data/screenshots/03@2.png 64 | 65 | 66 | Editing a category 67 | https://github.com/cjfloss/envelope/raw/master/data/screenshots/05.png 68 | https://github.com/cjfloss/envelope/raw/master/data/screenshots/05@2.png 69 | 70 | 71 | When you launch envelope for the first time 72 | https://github.com/cjfloss/envelope/raw/master/data/screenshots/01.png 73 | https://github.com/cjfloss/envelope/raw/master/data/screenshots/01@2.png 74 | 75 | 76 | Dialog to add an account 77 | https://github.com/cjfloss/envelope/raw/master/data/screenshots/02.png 78 | https://github.com/cjfloss/envelope/raw/master/data/screenshots/02@2.png 79 | 80 | 81 | Transactions View 82 | https://github.com/cjfloss/envelope/raw/master/data/screenshots/04.png 83 | https://github.com/cjfloss/envelope/raw/master/data/screenshots/04@2.png 84 | 85 | 86 | After creating a Account 87 | https://github.com/cjfloss/envelope/raw/master/data/screenshots/06.png 88 | https://github.com/cjfloss/envelope/raw/master/data/screenshots/06@2.png 89 | 90 | 91 | 92 | Cleiton Floss 93 | 94 | https://cjfloss.github.io/ 95 | https://github.com/cjfloss/envelope/issues 96 | https://github.com/cjfloss/envelope/issues 97 | cleitonfloss_AT_gmail.com 98 | 99 | 100 | 101 | 102 |

New:

103 |
    104 |
  • Polish Translation (By @DanyGee)
  • 105 |
  • French Translation (By @kameha)
  • 106 |
  • German Translation (By Nicolas @Mondei1)
  • 107 |
  • Drop SqlHeavy, use Sqlite3
  • 108 |
109 |

Fix:

110 |
    111 |
  • Use the right color on Budget overview
  • 112 |
  • Fix crash while importing
  • 113 |
  • Use better wording. Payee instead of Merchant
  • 114 |
  • Updated markdown license file
  • 115 |
  • Updated HoustonCI
  • 116 |
117 | ​
118 |
119 | 120 | 121 |

New:

122 |
    123 |
  • Improved add account Dialog
  • 124 |
  • Improved add category Dialog
  • 125 |
126 |

Fix:

127 |
    128 |
  • Fix crash while adding second account
  • 129 |
  • Allow resizing window
  • 130 |
  • Add unit tests
  • 131 |
  • Add current version to the binary
  • 132 |
133 | ​
134 |
135 | 136 | 137 |

Fixed some glitches:

138 |
    139 |
  • Fix crash while removing category
  • 140 |
  • Fix information loss on editing
  • 141 |
  • Add AppData changelog
  • 142 |
  • Update app branding
  • 143 |
144 |
145 |
146 | ​ 147 | 148 |

First release: Testing.

149 |

Basic functionalities:

150 |
    151 |
  • Add account
  • 152 |
  • Add transactions
  • 153 |
  • Import QIF files
  • 154 |
155 | ​
156 | ​
157 |
158 | 159 | 160 | Office 161 | Utility 162 | Finance 163 | 164 | 165 | 166 | #226324 167 | rgb(255, 255, 255) 168 | 5 169 | 170 |
171 | -------------------------------------------------------------------------------- /data/com.github.cjfloss.envelope.desktop.in: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=Envelope 3 | Comment=Manage your budget 4 | Keywords=Budget;Money; 5 | GenericName=Personal budget manager 6 | Exec=com.github.cjfloss.envelope 7 | Icon=com.github.cjfloss.envelope 8 | Terminal=false 9 | StartupNotify=true 10 | Type=Application 11 | Categories=Office;Utility;Finance; 12 | X-GNOME-Gettext-Domain=com.github.cjfloss.envelope 13 | X-GNOME-UsesNotifications=true; 14 | -------------------------------------------------------------------------------- /data/com.github.cjfloss.envelope.gschema.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | false 13 | Whether the window should be maximized 14 | Whether the window should be maximized by default 15 | 16 | 17 | 18 | 1200 19 | Window width 20 | Default window width 21 | 22 | 23 | 24 | 800 25 | Window height 26 | Default window height 27 | 28 | 29 | 30 | 250 31 | Width of the sidebar 32 | Default width of the sidebar 33 | 34 | 35 | 36 | -1 37 | Current selected account id 38 | Current selected account id in the sidebar 39 | 40 | 41 | 42 | -1 43 | Current selected category id 44 | Current selected category id in the sidebar 45 | 46 | 47 | 48 | "" 49 | Search term 50 | Default search term 51 | 52 | 53 | 54 | 'thismonth' 55 | Selected date filter in transaction view 56 | Default date filter in transaction view 57 | 58 | 59 | 60 | 0 61 | From date when date filter is set to manual 62 | Default from date when date filter is set to manual 63 | 64 | 65 | 66 | 0 67 | To date when date filter is set to manual 68 | Default to date when date filter is set to manual 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /data/icons/128x128/apps/com.github.cjfloss.envelope.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjfloss/envelope/0c0b212cf858a766208dac44d7b46d616e5362d3/data/icons/128x128/apps/com.github.cjfloss.envelope.png -------------------------------------------------------------------------------- /data/icons/16x16/apps/com.github.cjfloss.envelope.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjfloss/envelope/0c0b212cf858a766208dac44d7b46d616e5362d3/data/icons/16x16/apps/com.github.cjfloss.envelope.png -------------------------------------------------------------------------------- /data/icons/24x24/apps/com.github.cjfloss.envelope.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjfloss/envelope/0c0b212cf858a766208dac44d7b46d616e5362d3/data/icons/24x24/apps/com.github.cjfloss.envelope.png -------------------------------------------------------------------------------- /data/icons/32x32/apps/com.github.cjfloss.envelope.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjfloss/envelope/0c0b212cf858a766208dac44d7b46d616e5362d3/data/icons/32x32/apps/com.github.cjfloss.envelope.png -------------------------------------------------------------------------------- /data/icons/48x48/apps/com.github.cjfloss.envelope.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjfloss/envelope/0c0b212cf858a766208dac44d7b46d616e5362d3/data/icons/48x48/apps/com.github.cjfloss.envelope.png -------------------------------------------------------------------------------- /data/icons/64x64/apps/com.github.cjfloss.envelope.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjfloss/envelope/0c0b212cf858a766208dac44d7b46d616e5362d3/data/icons/64x64/apps/com.github.cjfloss.envelope.png -------------------------------------------------------------------------------- /data/meson.build: -------------------------------------------------------------------------------- 1 | meson.add_install_script ('post_install.py') 2 | 3 | install_subdir ( 4 | 'icons', 5 | install_dir: join_paths ( get_option ('datadir'), 'icons', 'hicolor'), 6 | strip_directory: true 7 | ) 8 | 9 | install_data ( 10 | meson.project_name () + '.gschema.xml', 11 | install_dir: join_paths (get_option ('datadir'), 'glib-2.0', 'schemas') 12 | ) 13 | 14 | desktop_file = i18n.merge_file ( 15 | 'desktop', 16 | input: meson.project_name () + '.desktop.in', 17 | output: meson.project_name () + '.desktop', 18 | install: true, 19 | install_dir: join_paths (get_option ('datadir'), 'applications'), 20 | po_dir: join_paths (meson.source_root (), 'po', 'extra'), 21 | type: 'desktop' 22 | ) 23 | 24 | appdata_file = i18n.merge_file ( 25 | 'appdata', 26 | input: meson.project_name () + '.appdata.xml.in', 27 | output: meson.project_name () + '.appdata.xml', 28 | install: true, 29 | install_dir: join_paths (get_option ('datadir'), 'metainfo'), 30 | po_dir: join_paths (meson.source_root (), 'po', 'extra') 31 | ) 32 | 33 | dfv = find_program ('desktop-file-validate', required: false) 34 | if dfv.found () 35 | test ( 36 | 'Validate desktop file', 37 | dfv, 38 | args: desktop_file.full_path() 39 | ) 40 | endif 41 | 42 | asc = find_program ('appstream-util', required: false) 43 | if asc.found () 44 | test ( 45 | 'Validate appdata file', 46 | asc, 47 | args: ['validate-relax', '--nonet', appdata_file.full_path()] 48 | ) 49 | endif 50 | 51 | vala_lint = find_program ('io.elementary.vala-lint', required: false) 52 | if vala_lint.found () 53 | test ( 54 | 'Vala lint', 55 | vala_lint, 56 | args: ['-d', join_paths (meson.source_root (), 'src')] 57 | ) 58 | endif 59 | -------------------------------------------------------------------------------- /data/post_install.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import environ, path 4 | from subprocess import call 5 | 6 | prefix = environ.get('MESON_INSTALL_PREFIX') 7 | datadir = path.join(prefix, 'share') 8 | destdir = environ.get('DESTDIR', '') 9 | 10 | # Package managers set this so we don't need to run 11 | if not destdir: 12 | print('Updating icon cache…') 13 | call(['gtk-update-icon-cache', '-qtf', path.join(datadir, 'icons', 'hicolor')]) 14 | 15 | print('Updating desktop database…') 16 | call(['update-desktop-database', '-q', path.join(datadir, 'applications')]) 17 | 18 | print('Compiling GSettings schemas…') 19 | call(['glib-compile-schemas', path.join(datadir, 'glib-2.0', 'schemas')]) -------------------------------------------------------------------------------- /data/screenshots/01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjfloss/envelope/0c0b212cf858a766208dac44d7b46d616e5362d3/data/screenshots/01.png -------------------------------------------------------------------------------- /data/screenshots/01@2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjfloss/envelope/0c0b212cf858a766208dac44d7b46d616e5362d3/data/screenshots/01@2.png -------------------------------------------------------------------------------- /data/screenshots/02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjfloss/envelope/0c0b212cf858a766208dac44d7b46d616e5362d3/data/screenshots/02.png -------------------------------------------------------------------------------- /data/screenshots/02@2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjfloss/envelope/0c0b212cf858a766208dac44d7b46d616e5362d3/data/screenshots/02@2.png -------------------------------------------------------------------------------- /data/screenshots/03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjfloss/envelope/0c0b212cf858a766208dac44d7b46d616e5362d3/data/screenshots/03.png -------------------------------------------------------------------------------- /data/screenshots/03@2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjfloss/envelope/0c0b212cf858a766208dac44d7b46d616e5362d3/data/screenshots/03@2.png -------------------------------------------------------------------------------- /data/screenshots/04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjfloss/envelope/0c0b212cf858a766208dac44d7b46d616e5362d3/data/screenshots/04.png -------------------------------------------------------------------------------- /data/screenshots/04@2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjfloss/envelope/0c0b212cf858a766208dac44d7b46d616e5362d3/data/screenshots/04@2.png -------------------------------------------------------------------------------- /data/screenshots/05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjfloss/envelope/0c0b212cf858a766208dac44d7b46d616e5362d3/data/screenshots/05.png -------------------------------------------------------------------------------- /data/screenshots/05@2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjfloss/envelope/0c0b212cf858a766208dac44d7b46d616e5362d3/data/screenshots/05@2.png -------------------------------------------------------------------------------- /data/screenshots/06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjfloss/envelope/0c0b212cf858a766208dac44d7b46d616e5362d3/data/screenshots/06.png -------------------------------------------------------------------------------- /data/screenshots/06@2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjfloss/envelope/0c0b212cf858a766208dac44d7b46d616e5362d3/data/screenshots/06@2.png -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | project ( 2 | 'com.github.cjfloss.envelope', 'vala', 'c', 3 | version: '0.0.4', 4 | license: 'GPL3', 5 | meson_version: '>= 0.47.0', 6 | default_options: ['warning_level=3'] 7 | ) 8 | 9 | add_global_arguments ( 10 | '-DGETTEXT_PACKAGE="' + meson.project_name () + '"', 11 | language: 'c' 12 | ) 13 | 14 | i18n = import ('i18n') 15 | gnome = import ('gnome') 16 | 17 | subdir ('src') 18 | subdir ('data') 19 | subdir ('po') 20 | subdir ('tests') -------------------------------------------------------------------------------- /po/LINGUAS: -------------------------------------------------------------------------------- 1 | de 2 | fr 3 | lt 4 | nl 5 | pl 6 | ru_RU 7 | -------------------------------------------------------------------------------- /po/POTFILES: -------------------------------------------------------------------------------- 1 | src/database/Database.vala 2 | src/database/DatabaseCursor.vala 3 | src/database/DatabaseManager.vala 4 | src/view/TransactionView.vala 5 | src/view/Sidebar.vala 6 | src/view/WelcomeScreen.vala 7 | src/view/AccountWelcomeScreen.vala 8 | src/view/BudgetOverview.vala 9 | src/view/FilterView.vala 10 | src/view/CategoryProperties.vala 11 | src/dialog/AddAccountDialog.vala 12 | src/dialog/AddCategoryDialog.vala 13 | src/dialog/ImportTransactionsDialog.vala 14 | src/widget/CellRendererCategoryPicker.vala 15 | src/window/MainWindow.vala 16 | src/Envelope.vala -------------------------------------------------------------------------------- /po/com.github.cjfloss.envelope.pot: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the com.github.cjfloss.envelope package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: com.github.cjfloss.envelope\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2019-05-04 18:34+0200\n" 12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 13 | "Last-Translator: FULL NAME \n" 14 | "Language-Team: LANGUAGE \n" 15 | "Language: \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=UTF-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | 20 | #: src/database/DatabaseManager.vala:581 21 | msgid "Groceries" 22 | msgstr "" 23 | 24 | #: src/database/DatabaseManager.vala:582 25 | msgid "Fuel" 26 | msgstr "" 27 | 28 | #: src/database/DatabaseManager.vala:583 29 | msgid "Public transit" 30 | msgstr "" 31 | 32 | #: src/database/DatabaseManager.vala:584 33 | msgid "Restaurants" 34 | msgstr "" 35 | 36 | #: src/database/DatabaseManager.vala:585 37 | msgid "Entertainment" 38 | msgstr "" 39 | 40 | #: src/database/DatabaseManager.vala:586 src/dialog/AddAccountDialog.vala:80 41 | msgid "Savings" 42 | msgstr "" 43 | 44 | #: src/database/DatabaseManager.vala:587 45 | msgid "Personal care" 46 | msgstr "" 47 | 48 | #: src/database/DatabaseManager.vala:588 49 | msgid "Alcohol & Bars" 50 | msgstr "" 51 | 52 | #: src/database/DatabaseManager.vala:589 53 | msgid "Emergency fund" 54 | msgstr "" 55 | 56 | #: src/view/TransactionView.vala:166 src/view/TransactionView.vala:864 57 | #: src/view/CategoryProperties.vala:143 58 | msgid "Apply" 59 | msgstr "" 60 | 61 | #: src/view/TransactionView.vala:177 src/view/TransactionView.vala:323 62 | #: src/view/TransactionView.vala:880 src/view/TransactionView.vala:898 63 | msgid "Add transaction" 64 | msgstr "" 65 | 66 | #: src/view/TransactionView.vala:300 67 | msgid "No results." 68 | msgstr "" 69 | 70 | #: src/view/TransactionView.vala:327 src/view/CategoryProperties.vala:139 71 | #: src/dialog/AddAccountDialog.vala:121 src/dialog/AddCategoryDialog.vala:89 72 | #: src/widget/CellRendererCategoryPicker.vala:81 73 | msgid "Cancel" 74 | msgstr "" 75 | 76 | #: src/view/TransactionView.vala:520 77 | msgid "Date" 78 | msgstr "" 79 | 80 | #: src/view/TransactionView.vala:533 81 | msgid "Payee" 82 | msgstr "" 83 | 84 | #: src/view/TransactionView.vala:546 85 | msgid "Category" 86 | msgstr "" 87 | 88 | #: src/view/TransactionView.vala:559 89 | #, c-format 90 | msgid "Outflow (%s)" 91 | msgstr "" 92 | 93 | #: src/view/TransactionView.vala:572 94 | #, c-format 95 | msgid "Inflow (%s)" 96 | msgstr "" 97 | 98 | #: src/view/TransactionView.vala:585 99 | msgid "Memo" 100 | msgstr "" 101 | 102 | #: src/view/TransactionView.vala:600 103 | msgid "Split" 104 | msgstr "" 105 | 106 | #: src/view/TransactionView.vala:602 src/view/Sidebar.vala:220 107 | msgid "Remove" 108 | msgstr "" 109 | 110 | #: src/view/TransactionView.vala:926 111 | msgid "Transaction removed" 112 | msgstr "" 113 | 114 | #: src/view/Sidebar.vala:252 115 | #, c-format 116 | msgid "Budget overview for %s" 117 | msgstr "" 118 | 119 | #: src/view/Sidebar.vala:254 120 | msgid "Spending this month" 121 | msgstr "" 122 | 123 | #: src/view/Sidebar.vala:254 124 | #, c-format 125 | msgid "Money spent in %s" 126 | msgstr "" 127 | 128 | #: src/view/Sidebar.vala:256 129 | msgid "Income this month" 130 | msgstr "" 131 | 132 | #: src/view/Sidebar.vala:256 133 | #, c-format 134 | msgid "Money earned in %s" 135 | msgstr "" 136 | 137 | #: src/view/Sidebar.vala:258 src/view/CategoryProperties.vala:124 138 | msgid "Remaining" 139 | msgstr "" 140 | 141 | #: src/view/Sidebar.vala:258 142 | #, c-format 143 | msgid "Remaining balance for %s" 144 | msgstr "" 145 | 146 | #: src/view/Sidebar.vala:261 147 | msgid "Accounts" 148 | msgstr "" 149 | 150 | #: src/view/Sidebar.vala:268 151 | msgid "Add account…" 152 | msgstr "" 153 | 154 | #: src/view/Sidebar.vala:268 155 | msgid "Add a new account" 156 | msgstr "" 157 | 158 | #: src/view/Sidebar.vala:271 159 | msgid "Spending categories" 160 | msgstr "" 161 | 162 | #: src/view/Sidebar.vala:371 src/window/MainWindow.vala:285 163 | msgid "Uncategorized" 164 | msgstr "" 165 | 166 | #: src/view/Sidebar.vala:385 167 | msgid "Add category…" 168 | msgstr "" 169 | 170 | #: src/view/Sidebar.vala:385 171 | msgid "Add a new spending category" 172 | msgstr "" 173 | 174 | #: src/view/Sidebar.vala:652 175 | msgid "None yet" 176 | msgstr "" 177 | 178 | #: src/view/Sidebar.vala:1033 179 | #, c-format 180 | msgid "Category %s removed" 181 | msgstr "" 182 | 183 | #: src/view/Sidebar.vala:1044 184 | #, c-format 185 | msgid "Account %s removed" 186 | msgstr "" 187 | 188 | #: src/view/WelcomeScreen.vala:26 189 | msgid "Get your budget going" 190 | msgstr "" 191 | 192 | #: src/view/WelcomeScreen.vala:26 src/view/WelcomeScreen.vala:27 193 | msgid "You have not configured any account yet" 194 | msgstr "" 195 | 196 | #: src/view/WelcomeScreen.vala:27 197 | msgid "Add an account" 198 | msgstr "" 199 | 200 | #: src/view/AccountWelcomeScreen.vala:41 201 | msgid "Spend! Get paid!" 202 | msgstr "" 203 | 204 | #: src/view/AccountWelcomeScreen.vala:41 205 | msgid "There are currently no transactions in this account" 206 | msgstr "" 207 | 208 | #: src/view/AccountWelcomeScreen.vala:47 209 | msgid "Record a transaction" 210 | msgstr "" 211 | 212 | #: src/view/AccountWelcomeScreen.vala:47 213 | msgid "Record a fresh new transaction for this account. Or, plan a future one!" 214 | msgstr "" 215 | 216 | #: src/view/AccountWelcomeScreen.vala:48 src/window/MainWindow.vala:105 217 | msgid "Import transactions" 218 | msgstr "" 219 | 220 | #: src/view/AccountWelcomeScreen.vala:48 221 | msgid "Import from a QIF file obtained from another application" 222 | msgstr "" 223 | 224 | #: src/view/BudgetOverview.vala:93 225 | msgid "Your budget right now:" 226 | msgstr "" 227 | 228 | #: src/view/BudgetOverview.vala:106 229 | msgid "Inflow:" 230 | msgstr "" 231 | 232 | #: src/view/BudgetOverview.vala:119 233 | msgid "Outflow:" 234 | msgstr "" 235 | 236 | #: src/view/BudgetOverview.vala:131 237 | msgid "Remaining this month:" 238 | msgstr "" 239 | 240 | #: src/view/FilterView.vala:82 241 | msgid "This month" 242 | msgstr "" 243 | 244 | #: src/view/FilterView.vala:86 245 | msgid "Last month" 246 | msgstr "" 247 | 248 | #: src/view/FilterView.vala:90 249 | msgid "Future" 250 | msgstr "" 251 | 252 | #: src/view/FilterView.vala:94 253 | msgid "Pick dates:" 254 | msgstr "" 255 | 256 | #: src/view/CategoryProperties.vala:76 257 | msgid "Edit category" 258 | msgstr "" 259 | 260 | #: src/view/CategoryProperties.vala:81 261 | msgid "Name" 262 | msgstr "" 263 | 264 | #: src/view/CategoryProperties.vala:91 265 | msgid "Budget" 266 | msgstr "" 267 | 268 | #: src/view/CategoryProperties.vala:98 269 | msgid "Monthly budget" 270 | msgstr "" 271 | 272 | #: src/view/CategoryProperties.vala:106 273 | msgid "Outflow" 274 | msgstr "" 275 | 276 | #: src/view/CategoryProperties.vala:115 277 | msgid "Inflow" 278 | msgstr "" 279 | 280 | #: src/dialog/AddAccountDialog.vala:35 281 | msgid "Add an Account" 282 | msgstr "" 283 | 284 | #: src/dialog/AddAccountDialog.vala:58 285 | msgid "Label:" 286 | msgstr "" 287 | 288 | #: src/dialog/AddAccountDialog.vala:63 289 | msgid "Eg.: account number" 290 | msgstr "" 291 | 292 | #: src/dialog/AddAccountDialog.vala:70 293 | msgid "Type:" 294 | msgstr "" 295 | 296 | #: src/dialog/AddAccountDialog.vala:78 297 | msgid "Checking" 298 | msgstr "" 299 | 300 | #: src/dialog/AddAccountDialog.vala:101 301 | msgid "Balance:" 302 | msgstr "" 303 | 304 | #: src/dialog/AddAccountDialog.vala:110 305 | msgid "Description:" 306 | msgstr "" 307 | 308 | #: src/dialog/AddAccountDialog.vala:116 309 | msgid "Optional" 310 | msgstr "" 311 | 312 | #: src/dialog/AddAccountDialog.vala:120 313 | msgid "Create Account" 314 | msgstr "" 315 | 316 | #: src/dialog/AddAccountDialog.vala:151 317 | #, c-format 318 | msgid "Account %s has been created" 319 | msgstr "" 320 | 321 | #: src/dialog/AddCategoryDialog.vala:31 322 | msgid "Add a Category" 323 | msgstr "" 324 | 325 | #: src/dialog/AddCategoryDialog.vala:55 326 | msgid "Name:" 327 | msgstr "" 328 | 329 | #: src/dialog/AddCategoryDialog.vala:60 330 | #: src/widget/CellRendererCategoryPicker.vala:72 331 | msgid "Category name" 332 | msgstr "" 333 | 334 | #: src/dialog/AddCategoryDialog.vala:67 335 | #, c-format 336 | msgid "Monthly Budget for %s" 337 | msgstr "" 338 | 339 | #: src/dialog/AddCategoryDialog.vala:73 340 | msgid "Budget:" 341 | msgstr "" 342 | 343 | #: src/dialog/AddCategoryDialog.vala:78 344 | msgid "Monthly budget for category" 345 | msgstr "" 346 | 347 | #: src/dialog/AddCategoryDialog.vala:81 348 | msgid "Create Category" 349 | msgstr "" 350 | 351 | #: src/dialog/ImportTransactionsDialog.vala:26 352 | msgid "Import transactions from file" 353 | msgstr "" 354 | 355 | #: src/dialog/ImportTransactionsDialog.vala:67 356 | #, c-format 357 | msgid "%d transactions imported in account %s" 358 | msgstr "" 359 | 360 | #: src/widget/CellRendererCategoryPicker.vala:39 361 | #: src/widget/CellRendererCategoryPicker.vala:77 362 | #, c-format 363 | msgid "Apply to all %s" 364 | msgstr "" 365 | 366 | #: src/widget/CellRendererCategoryPicker.vala:85 367 | msgid "Set category" 368 | msgstr "" 369 | 370 | #: src/window/MainWindow.vala:89 371 | msgid "Export…" 372 | msgstr "" 373 | 374 | #: src/window/MainWindow.vala:115 375 | msgid "Record transaction" 376 | msgstr "" 377 | 378 | #: src/window/MainWindow.vala:122 379 | msgid "Search transactions…" 380 | msgstr "" 381 | 382 | #: src/window/MainWindow.vala:290 383 | #, c-format 384 | msgid "Search in %s" 385 | msgstr "" 386 | 387 | #: src/window/MainWindow.vala:292 388 | msgid "Search uncategorized" 389 | msgstr "" 390 | 391 | #: src/window/MainWindow.vala:369 392 | msgid "Transaction recorded" 393 | msgstr "" 394 | 395 | #: src/window/MainWindow.vala:415 396 | #, c-format 397 | msgid "Transactions in %s" 398 | msgstr "" 399 | -------------------------------------------------------------------------------- /po/extra/LINGUAS: -------------------------------------------------------------------------------- 1 | de 2 | fr 3 | lt 4 | nl 5 | pl 6 | -------------------------------------------------------------------------------- /po/extra/POTFILES: -------------------------------------------------------------------------------- 1 | data/com.github.cjfloss.envelope.desktop.in 2 | data/com.github.cjfloss.envelope.appdata.xml.in -------------------------------------------------------------------------------- /po/extra/extra.pot: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the extra package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: extra\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2019-05-04 18:17+0200\n" 12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 13 | "Last-Translator: FULL NAME \n" 14 | "Language-Team: LANGUAGE \n" 15 | "Language: \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=CHARSET\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | 20 | #: data/com.github.cjfloss.envelope.desktop.in:3 21 | #: data/com.github.cjfloss.envelope.appdata.xml.in:7 22 | msgid "Envelope" 23 | msgstr "" 24 | 25 | #: data/com.github.cjfloss.envelope.desktop.in:4 26 | msgid "Manage your budget" 27 | msgstr "" 28 | 29 | #: data/com.github.cjfloss.envelope.desktop.in:5 30 | msgid "Budget;Money;" 31 | msgstr "" 32 | 33 | #: data/com.github.cjfloss.envelope.desktop.in:6 34 | #: data/com.github.cjfloss.envelope.appdata.xml.in:8 35 | msgid "Personal budget manager" 36 | msgstr "" 37 | 38 | #: data/com.github.cjfloss.envelope.desktop.in:8 39 | msgid "com.github.cjfloss.envelope" 40 | msgstr "" 41 | 42 | #: data/com.github.cjfloss.envelope.appdata.xml.in:42 43 | msgid "" 44 | "Maintain your personal budget by using the tried-and-true envelope system. " 45 | "Designate spending categories (envelopes) and distribute your monthly income " 46 | "into them. Configure accounts where you record all your transactions, then " 47 | "assign each of them to a category." 48 | msgstr "" 49 | 50 | #: data/com.github.cjfloss.envelope.appdata.xml.in:45 51 | msgid "Originally made by Nicolas Laplante." 52 | msgstr "" 53 | 54 | #: data/com.github.cjfloss.envelope.appdata.xml.in:46 55 | msgid "Features:" 56 | msgstr "" 57 | 58 | #: data/com.github.cjfloss.envelope.appdata.xml.in:48 59 | msgid "Envelope system budget workflow" 60 | msgstr "" 61 | 62 | #: data/com.github.cjfloss.envelope.appdata.xml.in:49 63 | msgid "Import transactions from QIF files" 64 | msgstr "" 65 | 66 | #: data/com.github.cjfloss.envelope.appdata.xml.in:61 67 | msgid "Budget Overview" 68 | msgstr "" 69 | 70 | #: data/com.github.cjfloss.envelope.appdata.xml.in:66 71 | msgid "Editing a category" 72 | msgstr "" 73 | 74 | #: data/com.github.cjfloss.envelope.appdata.xml.in:71 75 | msgid "When you launch envelope for the first time" 76 | msgstr "" 77 | 78 | #: data/com.github.cjfloss.envelope.appdata.xml.in:76 79 | msgid "Dialog to add an account" 80 | msgstr "" 81 | 82 | #: data/com.github.cjfloss.envelope.appdata.xml.in:81 83 | msgid "Transactions View" 84 | msgstr "" 85 | 86 | #: data/com.github.cjfloss.envelope.appdata.xml.in:86 87 | msgid "After creating a Account" 88 | msgstr "" 89 | 90 | #: data/com.github.cjfloss.envelope.appdata.xml.in:92 91 | msgid "Cleiton Floss" 92 | msgstr "" 93 | 94 | #: data/com.github.cjfloss.envelope.appdata.xml.in:102 95 | #: data/com.github.cjfloss.envelope.appdata.xml.in:121 96 | msgid "New:" 97 | msgstr "" 98 | 99 | #: data/com.github.cjfloss.envelope.appdata.xml.in:104 100 | msgid "Polish Translation (By @DanyGee)" 101 | msgstr "" 102 | 103 | #: data/com.github.cjfloss.envelope.appdata.xml.in:105 104 | msgid "French Translation (By @kameha)" 105 | msgstr "" 106 | 107 | #: data/com.github.cjfloss.envelope.appdata.xml.in:106 108 | msgid "German Translation (By Nicolas @Mondei1)" 109 | msgstr "" 110 | 111 | #: data/com.github.cjfloss.envelope.appdata.xml.in:107 112 | msgid "Drop SqlHeavy, use Sqlite3" 113 | msgstr "" 114 | 115 | #: data/com.github.cjfloss.envelope.appdata.xml.in:109 116 | #: data/com.github.cjfloss.envelope.appdata.xml.in:126 117 | msgid "Fix:" 118 | msgstr "" 119 | 120 | #: data/com.github.cjfloss.envelope.appdata.xml.in:111 121 | msgid "Use the right color on Budget overview" 122 | msgstr "" 123 | 124 | #: data/com.github.cjfloss.envelope.appdata.xml.in:112 125 | msgid "Fix crash while importing" 126 | msgstr "" 127 | 128 | #: data/com.github.cjfloss.envelope.appdata.xml.in:113 129 | msgid "Use better wording. Payee instead of Merchant" 130 | msgstr "" 131 | 132 | #: data/com.github.cjfloss.envelope.appdata.xml.in:114 133 | msgid "Updated markdown license file" 134 | msgstr "" 135 | 136 | #: data/com.github.cjfloss.envelope.appdata.xml.in:115 137 | msgid "Updated HoustonCI" 138 | msgstr "" 139 | 140 | #: data/com.github.cjfloss.envelope.appdata.xml.in:123 141 | msgid "Improved add account Dialog" 142 | msgstr "" 143 | 144 | #: data/com.github.cjfloss.envelope.appdata.xml.in:124 145 | msgid "Improved add category Dialog" 146 | msgstr "" 147 | 148 | #: data/com.github.cjfloss.envelope.appdata.xml.in:128 149 | msgid "Fix crash while adding second account" 150 | msgstr "" 151 | 152 | #: data/com.github.cjfloss.envelope.appdata.xml.in:129 153 | msgid "Allow resizing window" 154 | msgstr "" 155 | 156 | #: data/com.github.cjfloss.envelope.appdata.xml.in:130 157 | msgid "Add unit tests" 158 | msgstr "" 159 | 160 | #: data/com.github.cjfloss.envelope.appdata.xml.in:131 161 | msgid "Add current version to the binary" 162 | msgstr "" 163 | 164 | #: data/com.github.cjfloss.envelope.appdata.xml.in:137 165 | msgid "Fixed some glitches:" 166 | msgstr "" 167 | 168 | #: data/com.github.cjfloss.envelope.appdata.xml.in:139 169 | msgid "Fix crash while removing category" 170 | msgstr "" 171 | 172 | #: data/com.github.cjfloss.envelope.appdata.xml.in:140 173 | msgid "Fix information loss on editing" 174 | msgstr "" 175 | 176 | #: data/com.github.cjfloss.envelope.appdata.xml.in:141 177 | msgid "Add AppData changelog" 178 | msgstr "" 179 | 180 | #: data/com.github.cjfloss.envelope.appdata.xml.in:142 181 | msgid "Update app branding" 182 | msgstr "" 183 | 184 | #: data/com.github.cjfloss.envelope.appdata.xml.in:148 185 | msgid "First release: Testing." 186 | msgstr "" 187 | 188 | #: data/com.github.cjfloss.envelope.appdata.xml.in:149 189 | msgid "Basic functionalities:" 190 | msgstr "" 191 | 192 | #: data/com.github.cjfloss.envelope.appdata.xml.in:151 193 | msgid "Add account" 194 | msgstr "" 195 | 196 | #: data/com.github.cjfloss.envelope.appdata.xml.in:152 197 | msgid "Add transactions" 198 | msgstr "" 199 | 200 | #: data/com.github.cjfloss.envelope.appdata.xml.in:153 201 | msgid "Import QIF files" 202 | msgstr "" 203 | -------------------------------------------------------------------------------- /po/extra/fr.po: -------------------------------------------------------------------------------- 1 | # French translations for extra package. 2 | # Copyright (C) 2019 THE extra'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the extra package. 4 | # NathanBnm, 2019. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: extra\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2019-05-04 18:17+0200\n" 11 | "PO-Revision-Date: 2019-05-04 18:00+0200\n" 12 | "Last-Translator: NathanBnm\n" 13 | "Language-Team: none\n" 14 | "Language: fr\n" 15 | "MIME-Version: 1.0\n" 16 | "Content-Type: text/plain; charset=UTF-8\n" 17 | "Content-Transfer-Encoding: 8bit\n" 18 | "Plural-Forms: nplurals=2; plural=(n > 1);\n" 19 | 20 | #: data/com.github.cjfloss.envelope.desktop.in:3 21 | #: data/com.github.cjfloss.envelope.appdata.xml.in:7 22 | msgid "Envelope" 23 | msgstr "Enveloppe" 24 | 25 | #: data/com.github.cjfloss.envelope.desktop.in:4 26 | msgid "Manage your budget" 27 | msgstr "Gérez votre budget" 28 | 29 | #: data/com.github.cjfloss.envelope.desktop.in:5 30 | msgid "Budget;Money;" 31 | msgstr "Budget;Argent;" 32 | 33 | #: data/com.github.cjfloss.envelope.desktop.in:6 34 | #: data/com.github.cjfloss.envelope.appdata.xml.in:8 35 | msgid "Personal budget manager" 36 | msgstr "Gestionnaire de budget personnel" 37 | 38 | #: data/com.github.cjfloss.envelope.desktop.in:8 39 | msgid "com.github.cjfloss.envelope" 40 | msgstr "com.github.cjfloss.envelope" 41 | 42 | #: data/com.github.cjfloss.envelope.appdata.xml.in:42 43 | msgid "" 44 | "Maintain your personal budget by using the tried-and-true envelope system. " 45 | "Designate spending categories (envelopes) and distribute your monthly income " 46 | "into them. Configure accounts where you record all your transactions, then " 47 | "assign each of them to a category." 48 | msgstr "" 49 | "Maintenez votre budget personnel en utilisant le système d'enveloppes qui a " 50 | "fait ses preuves. Désignez des catégories de dépenses (enveloppes) et " 51 | "répartissez votre revenu mensuel entre elles. Configurez des comptes où vous " 52 | "enregistrez toutes vos transactions, puis affectez chacune d'entre elles à " 53 | "une catégorie." 54 | 55 | #: data/com.github.cjfloss.envelope.appdata.xml.in:45 56 | msgid "Originally made by Nicolas Laplante." 57 | msgstr "Créé à l'origine par Nicolas Laplante." 58 | 59 | #: data/com.github.cjfloss.envelope.appdata.xml.in:46 60 | msgid "Features:" 61 | msgstr "Fonctionnalités :" 62 | 63 | #: data/com.github.cjfloss.envelope.appdata.xml.in:48 64 | msgid "Envelope system budget workflow" 65 | msgstr "Système d'opérations en enveloppes" 66 | 67 | #: data/com.github.cjfloss.envelope.appdata.xml.in:49 68 | msgid "Import transactions from QIF files" 69 | msgstr "Importez des transactions depuis un fichier QIF" 70 | 71 | #: data/com.github.cjfloss.envelope.appdata.xml.in:61 72 | msgid "Budget Overview" 73 | msgstr "Aperçu du budget pour %s" 74 | 75 | #: data/com.github.cjfloss.envelope.appdata.xml.in:66 76 | msgid "Editing a category" 77 | msgstr "Modification d'une catégorie" 78 | 79 | #: data/com.github.cjfloss.envelope.appdata.xml.in:71 80 | msgid "When you launch envelope for the first time" 81 | msgstr "Au premier démarrage d'Enveloppe" 82 | 83 | #: data/com.github.cjfloss.envelope.appdata.xml.in:76 84 | msgid "Dialog to add an account" 85 | msgstr "Boîte de dialogue pour ajouter un compte" 86 | 87 | #: data/com.github.cjfloss.envelope.appdata.xml.in:81 88 | msgid "Transactions View" 89 | msgstr "Vue des transactions" 90 | 91 | #: data/com.github.cjfloss.envelope.appdata.xml.in:86 92 | msgid "After creating a Account" 93 | msgstr "Après avoir créé un compte" 94 | 95 | #: data/com.github.cjfloss.envelope.appdata.xml.in:92 96 | msgid "Cleiton Floss" 97 | msgstr "Cleiton Floss" 98 | 99 | #: data/com.github.cjfloss.envelope.appdata.xml.in:102 100 | #: data/com.github.cjfloss.envelope.appdata.xml.in:121 101 | msgid "New:" 102 | msgstr "Nouveau :" 103 | 104 | #: data/com.github.cjfloss.envelope.appdata.xml.in:104 105 | msgid "Polish Translation (By @DanyGee)" 106 | msgstr "" 107 | 108 | #: data/com.github.cjfloss.envelope.appdata.xml.in:105 109 | msgid "French Translation (By @kameha)" 110 | msgstr "" 111 | 112 | #: data/com.github.cjfloss.envelope.appdata.xml.in:106 113 | msgid "German Translation (By Nicolas @Mondei1)" 114 | msgstr "" 115 | 116 | #: data/com.github.cjfloss.envelope.appdata.xml.in:107 117 | msgid "Drop SqlHeavy, use Sqlite3" 118 | msgstr "" 119 | 120 | #: data/com.github.cjfloss.envelope.appdata.xml.in:109 121 | #: data/com.github.cjfloss.envelope.appdata.xml.in:126 122 | msgid "Fix:" 123 | msgstr "Correction :" 124 | 125 | #: data/com.github.cjfloss.envelope.appdata.xml.in:111 126 | msgid "Use the right color on Budget overview" 127 | msgstr "" 128 | 129 | #: data/com.github.cjfloss.envelope.appdata.xml.in:112 130 | msgid "Fix crash while importing" 131 | msgstr "Correction d'un plantage lors de l'importation" 132 | 133 | #: data/com.github.cjfloss.envelope.appdata.xml.in:113 134 | msgid "Use better wording. Payee instead of Merchant" 135 | msgstr "" 136 | 137 | #: data/com.github.cjfloss.envelope.appdata.xml.in:114 138 | msgid "Updated markdown license file" 139 | msgstr "" 140 | 141 | #: data/com.github.cjfloss.envelope.appdata.xml.in:115 142 | msgid "Updated HoustonCI" 143 | msgstr "" 144 | 145 | #: data/com.github.cjfloss.envelope.appdata.xml.in:123 146 | msgid "Improved add account Dialog" 147 | msgstr "Amélioration de la boîte de dialogue d'ajout de compte" 148 | 149 | #: data/com.github.cjfloss.envelope.appdata.xml.in:124 150 | msgid "Improved add category Dialog" 151 | msgstr "Amélioration de la boîte de dialogue d'ajoute de catégorie" 152 | 153 | #: data/com.github.cjfloss.envelope.appdata.xml.in:128 154 | msgid "Fix crash while adding second account" 155 | msgstr "Correction d'un plantage lors de l'ajout d'un deuxième compte" 156 | 157 | #: data/com.github.cjfloss.envelope.appdata.xml.in:129 158 | msgid "Allow resizing window" 159 | msgstr "La fenêtre est désormais redimensionnable" 160 | 161 | #: data/com.github.cjfloss.envelope.appdata.xml.in:130 162 | msgid "Add unit tests" 163 | msgstr "Ajout des tests unitaires" 164 | 165 | #: data/com.github.cjfloss.envelope.appdata.xml.in:131 166 | msgid "Add current version to the binary" 167 | msgstr "Ajout de la version actuelle au fichier binaire" 168 | 169 | #: data/com.github.cjfloss.envelope.appdata.xml.in:137 170 | msgid "Fixed some glitches:" 171 | msgstr "Corrections de bugs :" 172 | 173 | #: data/com.github.cjfloss.envelope.appdata.xml.in:139 174 | msgid "Fix crash while removing category" 175 | msgstr "Correction d'un plantage lors de la suppression d'un catégorie" 176 | 177 | #: data/com.github.cjfloss.envelope.appdata.xml.in:140 178 | msgid "Fix information loss on editing" 179 | msgstr "Correction de la perte d'informations lors d'une modification" 180 | 181 | #: data/com.github.cjfloss.envelope.appdata.xml.in:141 182 | msgid "Add AppData changelog" 183 | msgstr "Ajout des notes de version dans le fichier AppData" 184 | 185 | #: data/com.github.cjfloss.envelope.appdata.xml.in:142 186 | msgid "Update app branding" 187 | msgstr "Mise à jour du nom de l'application" 188 | 189 | #: data/com.github.cjfloss.envelope.appdata.xml.in:148 190 | msgid "First release: Testing." 191 | msgstr "Première version : Test." 192 | 193 | #: data/com.github.cjfloss.envelope.appdata.xml.in:149 194 | msgid "Basic functionalities:" 195 | msgstr "Fonctionnalités de base :" 196 | 197 | #: data/com.github.cjfloss.envelope.appdata.xml.in:151 198 | msgid "Add account" 199 | msgstr "Ajoutez un compte" 200 | 201 | #: data/com.github.cjfloss.envelope.appdata.xml.in:152 202 | msgid "Add transactions" 203 | msgstr "Ajoutez des transactions" 204 | 205 | #: data/com.github.cjfloss.envelope.appdata.xml.in:153 206 | msgid "Import QIF files" 207 | msgstr "Importez des fichiers QIF" 208 | -------------------------------------------------------------------------------- /po/extra/lt.po: -------------------------------------------------------------------------------- 1 | # Lithuanian translations for extra package. 2 | # Copyright (C) 2019 THE extra'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the extra package. 4 | # Moo, 2019. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: extra\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2019-05-04 18:17+0200\n" 11 | "PO-Revision-Date: 2019-05-04 18:00+0200\n" 12 | "Last-Translator: Moo\n" 13 | "Language-Team: none\n" 14 | "Language: lt\n" 15 | "MIME-Version: 1.0\n" 16 | "Content-Type: text/plain; charset=UTF-8\n" 17 | "Content-Transfer-Encoding: 8bit\n" 18 | "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && (n" 19 | "%100<10 || n%100>=20) ? 1 : 2);\n" 20 | 21 | #: data/com.github.cjfloss.envelope.desktop.in:3 22 | #: data/com.github.cjfloss.envelope.appdata.xml.in:7 23 | msgid "Envelope" 24 | msgstr "" 25 | 26 | #: data/com.github.cjfloss.envelope.desktop.in:4 27 | msgid "Manage your budget" 28 | msgstr "Tvarkyti savo biudžetą" 29 | 30 | #: data/com.github.cjfloss.envelope.desktop.in:5 31 | msgid "Budget;Money;" 32 | msgstr "Biudžetas;Pinigai;" 33 | 34 | #: data/com.github.cjfloss.envelope.desktop.in:6 35 | #: data/com.github.cjfloss.envelope.appdata.xml.in:8 36 | msgid "Personal budget manager" 37 | msgstr "Asmeninio biudžeto tvarkytuvė" 38 | 39 | #: data/com.github.cjfloss.envelope.desktop.in:8 40 | msgid "com.github.cjfloss.envelope" 41 | msgstr "" 42 | 43 | #: data/com.github.cjfloss.envelope.appdata.xml.in:42 44 | msgid "" 45 | "Maintain your personal budget by using the tried-and-true envelope system. " 46 | "Designate spending categories (envelopes) and distribute your monthly income " 47 | "into them. Configure accounts where you record all your transactions, then " 48 | "assign each of them to a category." 49 | msgstr "" 50 | 51 | #: data/com.github.cjfloss.envelope.appdata.xml.in:45 52 | msgid "Originally made by Nicolas Laplante." 53 | msgstr "" 54 | 55 | #: data/com.github.cjfloss.envelope.appdata.xml.in:46 56 | msgid "Features:" 57 | msgstr "" 58 | 59 | #: data/com.github.cjfloss.envelope.appdata.xml.in:48 60 | msgid "Envelope system budget workflow" 61 | msgstr "" 62 | 63 | #: data/com.github.cjfloss.envelope.appdata.xml.in:49 64 | msgid "Import transactions from QIF files" 65 | msgstr "" 66 | 67 | #: data/com.github.cjfloss.envelope.appdata.xml.in:61 68 | msgid "Budget Overview" 69 | msgstr "" 70 | 71 | #: data/com.github.cjfloss.envelope.appdata.xml.in:66 72 | msgid "Editing a category" 73 | msgstr "" 74 | 75 | #: data/com.github.cjfloss.envelope.appdata.xml.in:71 76 | msgid "When you launch envelope for the first time" 77 | msgstr "" 78 | 79 | #: data/com.github.cjfloss.envelope.appdata.xml.in:76 80 | msgid "Dialog to add an account" 81 | msgstr "" 82 | 83 | #: data/com.github.cjfloss.envelope.appdata.xml.in:81 84 | msgid "Transactions View" 85 | msgstr "" 86 | 87 | #: data/com.github.cjfloss.envelope.appdata.xml.in:86 88 | msgid "After creating a Account" 89 | msgstr "" 90 | 91 | #: data/com.github.cjfloss.envelope.appdata.xml.in:92 92 | msgid "Cleiton Floss" 93 | msgstr "" 94 | 95 | #: data/com.github.cjfloss.envelope.appdata.xml.in:102 96 | #: data/com.github.cjfloss.envelope.appdata.xml.in:121 97 | msgid "New:" 98 | msgstr "" 99 | 100 | #: data/com.github.cjfloss.envelope.appdata.xml.in:104 101 | msgid "Polish Translation (By @DanyGee)" 102 | msgstr "" 103 | 104 | #: data/com.github.cjfloss.envelope.appdata.xml.in:105 105 | msgid "French Translation (By @kameha)" 106 | msgstr "" 107 | 108 | #: data/com.github.cjfloss.envelope.appdata.xml.in:106 109 | msgid "German Translation (By Nicolas @Mondei1)" 110 | msgstr "" 111 | 112 | #: data/com.github.cjfloss.envelope.appdata.xml.in:107 113 | msgid "Drop SqlHeavy, use Sqlite3" 114 | msgstr "" 115 | 116 | #: data/com.github.cjfloss.envelope.appdata.xml.in:109 117 | #: data/com.github.cjfloss.envelope.appdata.xml.in:126 118 | msgid "Fix:" 119 | msgstr "" 120 | 121 | #: data/com.github.cjfloss.envelope.appdata.xml.in:111 122 | msgid "Use the right color on Budget overview" 123 | msgstr "" 124 | 125 | #: data/com.github.cjfloss.envelope.appdata.xml.in:112 126 | msgid "Fix crash while importing" 127 | msgstr "" 128 | 129 | #: data/com.github.cjfloss.envelope.appdata.xml.in:113 130 | msgid "Use better wording. Payee instead of Merchant" 131 | msgstr "" 132 | 133 | #: data/com.github.cjfloss.envelope.appdata.xml.in:114 134 | msgid "Updated markdown license file" 135 | msgstr "" 136 | 137 | #: data/com.github.cjfloss.envelope.appdata.xml.in:115 138 | msgid "Updated HoustonCI" 139 | msgstr "" 140 | 141 | #: data/com.github.cjfloss.envelope.appdata.xml.in:123 142 | msgid "Improved add account Dialog" 143 | msgstr "" 144 | 145 | #: data/com.github.cjfloss.envelope.appdata.xml.in:124 146 | msgid "Improved add category Dialog" 147 | msgstr "" 148 | 149 | #: data/com.github.cjfloss.envelope.appdata.xml.in:128 150 | msgid "Fix crash while adding second account" 151 | msgstr "" 152 | 153 | #: data/com.github.cjfloss.envelope.appdata.xml.in:129 154 | msgid "Allow resizing window" 155 | msgstr "" 156 | 157 | #: data/com.github.cjfloss.envelope.appdata.xml.in:130 158 | msgid "Add unit tests" 159 | msgstr "" 160 | 161 | #: data/com.github.cjfloss.envelope.appdata.xml.in:131 162 | msgid "Add current version to the binary" 163 | msgstr "" 164 | 165 | #: data/com.github.cjfloss.envelope.appdata.xml.in:137 166 | msgid "Fixed some glitches:" 167 | msgstr "" 168 | 169 | #: data/com.github.cjfloss.envelope.appdata.xml.in:139 170 | msgid "Fix crash while removing category" 171 | msgstr "" 172 | 173 | #: data/com.github.cjfloss.envelope.appdata.xml.in:140 174 | msgid "Fix information loss on editing" 175 | msgstr "" 176 | 177 | #: data/com.github.cjfloss.envelope.appdata.xml.in:141 178 | msgid "Add AppData changelog" 179 | msgstr "" 180 | 181 | #: data/com.github.cjfloss.envelope.appdata.xml.in:142 182 | msgid "Update app branding" 183 | msgstr "" 184 | 185 | #: data/com.github.cjfloss.envelope.appdata.xml.in:148 186 | msgid "First release: Testing." 187 | msgstr "" 188 | 189 | #: data/com.github.cjfloss.envelope.appdata.xml.in:149 190 | msgid "Basic functionalities:" 191 | msgstr "" 192 | 193 | #: data/com.github.cjfloss.envelope.appdata.xml.in:151 194 | msgid "Add account" 195 | msgstr "" 196 | 197 | #: data/com.github.cjfloss.envelope.appdata.xml.in:152 198 | msgid "Add transactions" 199 | msgstr "" 200 | 201 | #: data/com.github.cjfloss.envelope.appdata.xml.in:153 202 | msgid "Import QIF files" 203 | msgstr "" 204 | -------------------------------------------------------------------------------- /po/extra/meson.build: -------------------------------------------------------------------------------- 1 | i18n.gettext('extra', 2 | args: [ 3 | '--directory=' + meson.source_root(), 4 | '--from-code=UTF-8' 5 | ], 6 | install: false 7 | ) -------------------------------------------------------------------------------- /po/extra/nl.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the extra package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: extra\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2019-05-04 18:17+0200\n" 11 | "PO-Revision-Date: 2019-11-03 20:12+0100\n" 12 | "Language-Team: \n" 13 | "MIME-Version: 1.0\n" 14 | "Content-Type: text/plain; charset=UTF-8\n" 15 | "Content-Transfer-Encoding: 8bit\n" 16 | "X-Generator: Poedit 2.2.4\n" 17 | "Last-Translator: Heimen Stoffels \n" 18 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 19 | "Language: nl\n" 20 | 21 | #: data/com.github.cjfloss.envelope.desktop.in:3 22 | #: data/com.github.cjfloss.envelope.appdata.xml.in:7 23 | msgid "Envelope" 24 | msgstr "Envelop" 25 | 26 | #: data/com.github.cjfloss.envelope.desktop.in:4 27 | msgid "Manage your budget" 28 | msgstr "Beheer je budget" 29 | 30 | #: data/com.github.cjfloss.envelope.desktop.in:5 31 | msgid "Budget;Money;" 32 | msgstr "Budget;Geld;" 33 | 34 | #: data/com.github.cjfloss.envelope.desktop.in:6 35 | #: data/com.github.cjfloss.envelope.appdata.xml.in:8 36 | msgid "Personal budget manager" 37 | msgstr "Persoonlijk budgetbeheer" 38 | 39 | #: data/com.github.cjfloss.envelope.desktop.in:8 40 | msgid "com.github.cjfloss.envelope" 41 | msgstr "com.github.cjfloss.envelope" 42 | 43 | #: data/com.github.cjfloss.envelope.appdata.xml.in:42 44 | msgid "" 45 | "Maintain your personal budget by using the tried-and-true envelope system. " 46 | "Designate spending categories (envelopes) and distribute your monthly income " 47 | "into them. Configure accounts where you record all your transactions, then " 48 | "assign each of them to a category." 49 | msgstr "" 50 | "Beheer je persoonlijke budget middels het oude vertrouwde envelopsysteem. " 51 | "Wijs uitgavecategorieën toe ('enveloppen') en deel je maandelijkse inkomen " 52 | "hierin in. Stel rekeningen in waarop alle transacties plaatsvinden, en voeg " 53 | "ze dan toe aan categorieën." 54 | 55 | #: data/com.github.cjfloss.envelope.appdata.xml.in:45 56 | msgid "Originally made by Nicolas Laplante." 57 | msgstr "Oorspronkelijk gemaakt door Nicolas Laplante." 58 | 59 | #: data/com.github.cjfloss.envelope.appdata.xml.in:46 60 | msgid "Features:" 61 | msgstr "Mogelijkheden:" 62 | 63 | #: data/com.github.cjfloss.envelope.appdata.xml.in:48 64 | msgid "Envelope system budget workflow" 65 | msgstr "Envelopsysteem-budgetwerkwijze" 66 | 67 | #: data/com.github.cjfloss.envelope.appdata.xml.in:49 68 | msgid "Import transactions from QIF files" 69 | msgstr "Importeer transacties uit QIF-bestanden" 70 | 71 | #: data/com.github.cjfloss.envelope.appdata.xml.in:61 72 | msgid "Budget Overview" 73 | msgstr "Budgetoverzicht" 74 | 75 | #: data/com.github.cjfloss.envelope.appdata.xml.in:66 76 | msgid "Editing a category" 77 | msgstr "Categoriebewerking" 78 | 79 | #: data/com.github.cjfloss.envelope.appdata.xml.in:71 80 | msgid "When you launch envelope for the first time" 81 | msgstr "Voor het eerst geopende envelop" 82 | 83 | #: data/com.github.cjfloss.envelope.appdata.xml.in:76 84 | msgid "Dialog to add an account" 85 | msgstr "Venster om een rekening toe te voegen" 86 | 87 | #: data/com.github.cjfloss.envelope.appdata.xml.in:81 88 | msgid "Transactions View" 89 | msgstr "Transactie-overzicht" 90 | 91 | #: data/com.github.cjfloss.envelope.appdata.xml.in:86 92 | msgid "After creating a Account" 93 | msgstr "Na het toevoegen van een rekening" 94 | 95 | #: data/com.github.cjfloss.envelope.appdata.xml.in:92 96 | msgid "Cleiton Floss" 97 | msgstr "Cleiton Floss" 98 | 99 | #: data/com.github.cjfloss.envelope.appdata.xml.in:102 100 | #: data/com.github.cjfloss.envelope.appdata.xml.in:121 101 | msgid "New:" 102 | msgstr "Nieuw:" 103 | 104 | #: data/com.github.cjfloss.envelope.appdata.xml.in:104 105 | msgid "Polish Translation (By @DanyGee)" 106 | msgstr "Poolse vertaling, met dank aan @DanyGee" 107 | 108 | #: data/com.github.cjfloss.envelope.appdata.xml.in:105 109 | msgid "French Translation (By @kameha)" 110 | msgstr "Franse vertaling, met dank aan @kameha" 111 | 112 | #: data/com.github.cjfloss.envelope.appdata.xml.in:106 113 | msgid "German Translation (By Nicolas @Mondei1)" 114 | msgstr "Duitse vertaling, met dank aan Nicolas (@Mondei1)" 115 | 116 | #: data/com.github.cjfloss.envelope.appdata.xml.in:107 117 | msgid "Drop SqlHeavy, use Sqlite3" 118 | msgstr "SqlHeavy vervangen door Sqlite3" 119 | 120 | #: data/com.github.cjfloss.envelope.appdata.xml.in:109 121 | #: data/com.github.cjfloss.envelope.appdata.xml.in:126 122 | msgid "Fix:" 123 | msgstr "Opgelost:" 124 | 125 | #: data/com.github.cjfloss.envelope.appdata.xml.in:111 126 | msgid "Use the right color on Budget overview" 127 | msgstr "De juiste kleur wordt nu gebruikt op het budgetoverzicht" 128 | 129 | #: data/com.github.cjfloss.envelope.appdata.xml.in:112 130 | msgid "Fix crash while importing" 131 | msgstr "Crash opgelost tijdens het importeren" 132 | 133 | #: data/com.github.cjfloss.envelope.appdata.xml.in:113 134 | msgid "Use better wording. Payee instead of Merchant" 135 | msgstr "Betere bewoording gebruikt" 136 | 137 | #: data/com.github.cjfloss.envelope.appdata.xml.in:114 138 | msgid "Updated markdown license file" 139 | msgstr "Markdown-licentiebestand bijgewerkt" 140 | 141 | #: data/com.github.cjfloss.envelope.appdata.xml.in:115 142 | msgid "Updated HoustonCI" 143 | msgstr "HoustonCI bijgewerkt" 144 | 145 | #: data/com.github.cjfloss.envelope.appdata.xml.in:123 146 | msgid "Improved add account Dialog" 147 | msgstr "Verbeterd venster 'Rekening toevoegen'" 148 | 149 | #: data/com.github.cjfloss.envelope.appdata.xml.in:124 150 | msgid "Improved add category Dialog" 151 | msgstr "Verbeterd venster 'Categorie toevoegen'" 152 | 153 | #: data/com.github.cjfloss.envelope.appdata.xml.in:128 154 | msgid "Fix crash while adding second account" 155 | msgstr "Crash opgelost tijdens het toevoegen van een tweede account" 156 | 157 | #: data/com.github.cjfloss.envelope.appdata.xml.in:129 158 | msgid "Allow resizing window" 159 | msgstr "De venstergrootte kan nu worden aangepast" 160 | 161 | #: data/com.github.cjfloss.envelope.appdata.xml.in:130 162 | msgid "Add unit tests" 163 | msgstr "Unittests toegevoegd" 164 | 165 | #: data/com.github.cjfloss.envelope.appdata.xml.in:131 166 | msgid "Add current version to the binary" 167 | msgstr "Huidige versie toegevoegd aan toepassingsbestand" 168 | 169 | #: data/com.github.cjfloss.envelope.appdata.xml.in:137 170 | msgid "Fixed some glitches:" 171 | msgstr "Enkele schoonheidsfoutjes opgelost:" 172 | 173 | #: data/com.github.cjfloss.envelope.appdata.xml.in:139 174 | msgid "Fix crash while removing category" 175 | msgstr "Crash opgelost tijdens het verwijderen van een categorie" 176 | 177 | #: data/com.github.cjfloss.envelope.appdata.xml.in:140 178 | msgid "Fix information loss on editing" 179 | msgstr "Oplossing voor informatieverlies tijdens bewerken" 180 | 181 | #: data/com.github.cjfloss.envelope.appdata.xml.in:141 182 | msgid "Add AppData changelog" 183 | msgstr "AppData-wijzigingslog toegevoegd" 184 | 185 | #: data/com.github.cjfloss.envelope.appdata.xml.in:142 186 | msgid "Update app branding" 187 | msgstr "Merk bijgewerkt" 188 | 189 | #: data/com.github.cjfloss.envelope.appdata.xml.in:148 190 | msgid "First release: Testing." 191 | msgstr "Eerste uitgave: test 1-2-3." 192 | 193 | #: data/com.github.cjfloss.envelope.appdata.xml.in:149 194 | msgid "Basic functionalities:" 195 | msgstr "Basisfunctionaliteit:" 196 | 197 | #: data/com.github.cjfloss.envelope.appdata.xml.in:151 198 | msgid "Add account" 199 | msgstr "Rekening toevoegen" 200 | 201 | #: data/com.github.cjfloss.envelope.appdata.xml.in:152 202 | msgid "Add transactions" 203 | msgstr "Transacties toevoegen" 204 | 205 | #: data/com.github.cjfloss.envelope.appdata.xml.in:153 206 | msgid "Import QIF files" 207 | msgstr "QIF-bestanden importeren" 208 | -------------------------------------------------------------------------------- /po/extra/pl.po: -------------------------------------------------------------------------------- 1 | # Polish translations for extra package. 2 | # Copyright (C) 2019 THE extra'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the extra package. 4 | # Automatically generated, 2019. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: extra\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2019-05-04 18:17+0200\n" 11 | "PO-Revision-Date: 2019-05-04 18:00+0200\n" 12 | "Last-Translator: Automatically generated\n" 13 | "Language-Team: none\n" 14 | "Language: pl\n" 15 | "MIME-Version: 1.0\n" 16 | "Content-Type: text/plain; charset=UTF-8\n" 17 | "Content-Transfer-Encoding: 8bit\n" 18 | "Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 " 19 | "|| n%100>=20) ? 1 : 2);\n" 20 | 21 | #: data/com.github.cjfloss.envelope.desktop.in:3 22 | #: data/com.github.cjfloss.envelope.appdata.xml.in:7 23 | msgid "Envelope" 24 | msgstr "" 25 | 26 | #: data/com.github.cjfloss.envelope.desktop.in:4 27 | msgid "Manage your budget" 28 | msgstr "Zarządzaj swoim budżetem" 29 | 30 | #: data/com.github.cjfloss.envelope.desktop.in:5 31 | msgid "Budget;Money;" 32 | msgstr "" 33 | 34 | #: data/com.github.cjfloss.envelope.desktop.in:6 35 | #: data/com.github.cjfloss.envelope.appdata.xml.in:8 36 | msgid "Personal budget manager" 37 | msgstr "" 38 | 39 | #: data/com.github.cjfloss.envelope.desktop.in:8 40 | msgid "com.github.cjfloss.envelope" 41 | msgstr "" 42 | 43 | #: data/com.github.cjfloss.envelope.appdata.xml.in:42 44 | msgid "" 45 | "Maintain your personal budget by using the tried-and-true envelope system. " 46 | "Designate spending categories (envelopes) and distribute your monthly income " 47 | "into them. Configure accounts where you record all your transactions, then " 48 | "assign each of them to a category." 49 | msgstr "" 50 | 51 | #: data/com.github.cjfloss.envelope.appdata.xml.in:45 52 | msgid "Originally made by Nicolas Laplante." 53 | msgstr "" 54 | 55 | #: data/com.github.cjfloss.envelope.appdata.xml.in:46 56 | msgid "Features:" 57 | msgstr "" 58 | 59 | #: data/com.github.cjfloss.envelope.appdata.xml.in:48 60 | msgid "Envelope system budget workflow" 61 | msgstr "" 62 | 63 | #: data/com.github.cjfloss.envelope.appdata.xml.in:49 64 | msgid "Import transactions from QIF files" 65 | msgstr "" 66 | 67 | #: data/com.github.cjfloss.envelope.appdata.xml.in:61 68 | msgid "Budget Overview" 69 | msgstr "" 70 | 71 | #: data/com.github.cjfloss.envelope.appdata.xml.in:66 72 | msgid "Editing a category" 73 | msgstr "" 74 | 75 | #: data/com.github.cjfloss.envelope.appdata.xml.in:71 76 | msgid "When you launch envelope for the first time" 77 | msgstr "" 78 | 79 | #: data/com.github.cjfloss.envelope.appdata.xml.in:76 80 | msgid "Dialog to add an account" 81 | msgstr "" 82 | 83 | #: data/com.github.cjfloss.envelope.appdata.xml.in:81 84 | msgid "Transactions View" 85 | msgstr "" 86 | 87 | #: data/com.github.cjfloss.envelope.appdata.xml.in:86 88 | msgid "After creating a Account" 89 | msgstr "" 90 | 91 | #: data/com.github.cjfloss.envelope.appdata.xml.in:92 92 | msgid "Cleiton Floss" 93 | msgstr "" 94 | 95 | #: data/com.github.cjfloss.envelope.appdata.xml.in:102 96 | #: data/com.github.cjfloss.envelope.appdata.xml.in:121 97 | msgid "New:" 98 | msgstr "" 99 | 100 | #: data/com.github.cjfloss.envelope.appdata.xml.in:104 101 | msgid "Polish Translation (By @DanyGee)" 102 | msgstr "" 103 | 104 | #: data/com.github.cjfloss.envelope.appdata.xml.in:105 105 | msgid "French Translation (By @kameha)" 106 | msgstr "" 107 | 108 | #: data/com.github.cjfloss.envelope.appdata.xml.in:106 109 | msgid "German Translation (By Nicolas @Mondei1)" 110 | msgstr "" 111 | 112 | #: data/com.github.cjfloss.envelope.appdata.xml.in:107 113 | msgid "Drop SqlHeavy, use Sqlite3" 114 | msgstr "" 115 | 116 | #: data/com.github.cjfloss.envelope.appdata.xml.in:109 117 | #: data/com.github.cjfloss.envelope.appdata.xml.in:126 118 | msgid "Fix:" 119 | msgstr "" 120 | 121 | #: data/com.github.cjfloss.envelope.appdata.xml.in:111 122 | msgid "Use the right color on Budget overview" 123 | msgstr "" 124 | 125 | #: data/com.github.cjfloss.envelope.appdata.xml.in:112 126 | msgid "Fix crash while importing" 127 | msgstr "" 128 | 129 | #: data/com.github.cjfloss.envelope.appdata.xml.in:113 130 | msgid "Use better wording. Payee instead of Merchant" 131 | msgstr "" 132 | 133 | #: data/com.github.cjfloss.envelope.appdata.xml.in:114 134 | msgid "Updated markdown license file" 135 | msgstr "" 136 | 137 | #: data/com.github.cjfloss.envelope.appdata.xml.in:115 138 | msgid "Updated HoustonCI" 139 | msgstr "" 140 | 141 | #: data/com.github.cjfloss.envelope.appdata.xml.in:123 142 | msgid "Improved add account Dialog" 143 | msgstr "" 144 | 145 | #: data/com.github.cjfloss.envelope.appdata.xml.in:124 146 | msgid "Improved add category Dialog" 147 | msgstr "" 148 | 149 | #: data/com.github.cjfloss.envelope.appdata.xml.in:128 150 | msgid "Fix crash while adding second account" 151 | msgstr "" 152 | 153 | #: data/com.github.cjfloss.envelope.appdata.xml.in:129 154 | msgid "Allow resizing window" 155 | msgstr "" 156 | 157 | #: data/com.github.cjfloss.envelope.appdata.xml.in:130 158 | msgid "Add unit tests" 159 | msgstr "" 160 | 161 | #: data/com.github.cjfloss.envelope.appdata.xml.in:131 162 | msgid "Add current version to the binary" 163 | msgstr "" 164 | 165 | #: data/com.github.cjfloss.envelope.appdata.xml.in:137 166 | msgid "Fixed some glitches:" 167 | msgstr "" 168 | 169 | #: data/com.github.cjfloss.envelope.appdata.xml.in:139 170 | msgid "Fix crash while removing category" 171 | msgstr "" 172 | 173 | #: data/com.github.cjfloss.envelope.appdata.xml.in:140 174 | msgid "Fix information loss on editing" 175 | msgstr "" 176 | 177 | #: data/com.github.cjfloss.envelope.appdata.xml.in:141 178 | msgid "Add AppData changelog" 179 | msgstr "" 180 | 181 | #: data/com.github.cjfloss.envelope.appdata.xml.in:142 182 | msgid "Update app branding" 183 | msgstr "" 184 | 185 | #: data/com.github.cjfloss.envelope.appdata.xml.in:148 186 | msgid "First release: Testing." 187 | msgstr "" 188 | 189 | #: data/com.github.cjfloss.envelope.appdata.xml.in:149 190 | msgid "Basic functionalities:" 191 | msgstr "" 192 | 193 | #: data/com.github.cjfloss.envelope.appdata.xml.in:151 194 | msgid "Add account" 195 | msgstr "" 196 | 197 | #: data/com.github.cjfloss.envelope.appdata.xml.in:152 198 | msgid "Add transactions" 199 | msgstr "" 200 | 201 | #: data/com.github.cjfloss.envelope.appdata.xml.in:153 202 | msgid "Import QIF files" 203 | msgstr "" 204 | -------------------------------------------------------------------------------- /po/meson.build: -------------------------------------------------------------------------------- 1 | i18n.gettext(meson.project_name(), 2 | args: [ 3 | '--directory=' + meson.source_root(), 4 | '--from-code=UTF-8' 5 | ] 6 | ) 7 | 8 | subdir('extra') -------------------------------------------------------------------------------- /po/pl.po: -------------------------------------------------------------------------------- 1 | # Polish translations for com.github.cjfloss.envelope package. 2 | # Copyright (C) 2019 THE com.github.cjfloss.envelope'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the com.github.cjfloss.envelope package. 4 | # Automatically generated, 2019. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: com.github.cjfloss.envelope\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2019-05-04 18:34+0200\n" 11 | "PO-Revision-Date: 2019-05-04 17:59+0200\n" 12 | "Last-Translator: Automatically generated\n" 13 | "Language-Team: none\n" 14 | "Language: pl\n" 15 | "MIME-Version: 1.0\n" 16 | "Content-Type: text/plain; charset=UTF-8\n" 17 | "Content-Transfer-Encoding: 8bit\n" 18 | "Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 " 19 | "|| n%100>=20) ? 1 : 2);\n" 20 | 21 | #: src/database/DatabaseManager.vala:581 22 | msgid "Groceries" 23 | msgstr "Pożywienie" 24 | 25 | #: src/database/DatabaseManager.vala:582 26 | msgid "Fuel" 27 | msgstr "Paliwo" 28 | 29 | #: src/database/DatabaseManager.vala:583 30 | msgid "Public transit" 31 | msgstr "Transport publiczny" 32 | 33 | #: src/database/DatabaseManager.vala:584 34 | msgid "Restaurants" 35 | msgstr "Restauracje" 36 | 37 | #: src/database/DatabaseManager.vala:585 38 | msgid "Entertainment" 39 | msgstr "Rozrywka" 40 | 41 | #: src/database/DatabaseManager.vala:586 src/dialog/AddAccountDialog.vala:80 42 | msgid "Savings" 43 | msgstr "Oszczędności" 44 | 45 | #: src/database/DatabaseManager.vala:587 46 | msgid "Personal care" 47 | msgstr "Higienia osobista" 48 | 49 | #: src/database/DatabaseManager.vala:588 50 | msgid "Alcohol & Bars" 51 | msgstr "Alkohol i bary" 52 | 53 | #: src/database/DatabaseManager.vala:589 54 | msgid "Emergency fund" 55 | msgstr "Na czarną godzinę" 56 | 57 | #: src/view/TransactionView.vala:166 src/view/TransactionView.vala:864 58 | #: src/view/CategoryProperties.vala:143 59 | msgid "Apply" 60 | msgstr "Zatwierdź" 61 | 62 | #: src/view/TransactionView.vala:177 src/view/TransactionView.vala:323 63 | #: src/view/TransactionView.vala:880 src/view/TransactionView.vala:898 64 | msgid "Add transaction" 65 | msgstr "Dodaj transakcję" 66 | 67 | #: src/view/TransactionView.vala:300 68 | msgid "No results." 69 | msgstr "Brak wyników" 70 | 71 | #: src/view/TransactionView.vala:327 src/view/CategoryProperties.vala:139 72 | #: src/dialog/AddAccountDialog.vala:121 src/dialog/AddCategoryDialog.vala:89 73 | #: src/widget/CellRendererCategoryPicker.vala:81 74 | msgid "Cancel" 75 | msgstr "Anuluj" 76 | 77 | #: src/view/TransactionView.vala:520 78 | msgid "Date" 79 | msgstr "Data" 80 | 81 | #: src/view/TransactionView.vala:533 82 | msgid "Payee" 83 | msgstr "" 84 | 85 | #: src/view/TransactionView.vala:546 86 | msgid "Category" 87 | msgstr "Kategoria" 88 | 89 | #: src/view/TransactionView.vala:559 90 | #, c-format 91 | msgid "Outflow (%s)" 92 | msgstr "Wydatek (%s)" 93 | 94 | #: src/view/TransactionView.vala:572 95 | #, c-format 96 | msgid "Inflow (%s)" 97 | msgstr "Przychód (%s)" 98 | 99 | #: src/view/TransactionView.vala:585 100 | msgid "Memo" 101 | msgstr "Notatka" 102 | 103 | #: src/view/TransactionView.vala:600 104 | msgid "Split" 105 | msgstr "Podziel" 106 | 107 | #: src/view/TransactionView.vala:602 src/view/Sidebar.vala:220 108 | msgid "Remove" 109 | msgstr "Usuń" 110 | 111 | #: src/view/TransactionView.vala:926 112 | msgid "Transaction removed" 113 | msgstr "Transakcja usunięta" 114 | 115 | #: src/view/Sidebar.vala:252 116 | #, c-format 117 | msgid "Budget overview for %s" 118 | msgstr "Przegląd budżetu dla %s" 119 | 120 | #: src/view/Sidebar.vala:254 121 | msgid "Spending this month" 122 | msgstr "Wydano w tym miesiącu" 123 | 124 | #: src/view/Sidebar.vala:254 125 | #, c-format 126 | msgid "Money spent in %s" 127 | msgstr "Pieniądze wydane w %s" 128 | 129 | #: src/view/Sidebar.vala:256 130 | msgid "Income this month" 131 | msgstr "Przychód w tym miesiącu" 132 | 133 | #: src/view/Sidebar.vala:256 134 | #, c-format 135 | msgid "Money earned in %s" 136 | msgstr "Pieniądze zarobione w %s" 137 | 138 | #: src/view/Sidebar.vala:258 src/view/CategoryProperties.vala:124 139 | msgid "Remaining" 140 | msgstr "Pozostało" 141 | 142 | #: src/view/Sidebar.vala:258 143 | #, c-format 144 | msgid "Remaining balance for %s" 145 | msgstr "Pozostały bilans dla %s" 146 | 147 | #: src/view/Sidebar.vala:261 148 | msgid "Accounts" 149 | msgstr "Konta" 150 | 151 | #: src/view/Sidebar.vala:268 152 | msgid "Add account…" 153 | msgstr "Dodaj konto…" 154 | 155 | #: src/view/Sidebar.vala:268 156 | msgid "Add a new account" 157 | msgstr "Dodaj nowe konto" 158 | 159 | #: src/view/Sidebar.vala:271 160 | msgid "Spending categories" 161 | msgstr "Kategorie wydatków" 162 | 163 | #: src/view/Sidebar.vala:371 src/window/MainWindow.vala:285 164 | msgid "Uncategorized" 165 | msgstr "Nieskategoryzowane" 166 | 167 | #: src/view/Sidebar.vala:385 168 | msgid "Add category…" 169 | msgstr "Dodaj kategorię…" 170 | 171 | #: src/view/Sidebar.vala:385 172 | msgid "Add a new spending category" 173 | msgstr "Dodaj nową kategorię wydatkową" 174 | 175 | #: src/view/Sidebar.vala:652 176 | msgid "None yet" 177 | msgstr "Jeszcze nic nie ma" 178 | 179 | #: src/view/Sidebar.vala:1033 180 | #, c-format 181 | msgid "Category %s removed" 182 | msgstr "Kategoria %s usunięta" 183 | 184 | #: src/view/Sidebar.vala:1044 185 | #, c-format 186 | msgid "Account %s removed" 187 | msgstr "Konto %s usunięte" 188 | 189 | #: src/view/WelcomeScreen.vala:26 190 | #, fuzzy 191 | msgid "Get your budget going" 192 | msgstr "Twój aktualny budżet:" 193 | 194 | #: src/view/WelcomeScreen.vala:26 src/view/WelcomeScreen.vala:27 195 | msgid "You have not configured any account yet" 196 | msgstr "Nie skonfigurowałeś jeszcze żadnego konta" 197 | 198 | #: src/view/WelcomeScreen.vala:27 199 | msgid "Add an account" 200 | msgstr "Dodaj konto" 201 | 202 | #: src/view/AccountWelcomeScreen.vala:41 203 | msgid "Spend! Get paid!" 204 | msgstr "Wydawaj! Zarabiaj!" 205 | 206 | #: src/view/AccountWelcomeScreen.vala:41 207 | msgid "There are currently no transactions in this account" 208 | msgstr "Aktualnie brak transakcji dla tego konta" 209 | 210 | #: src/view/AccountWelcomeScreen.vala:47 211 | msgid "Record a transaction" 212 | msgstr "Zanotuj transakcję" 213 | 214 | #: src/view/AccountWelcomeScreen.vala:47 215 | msgid "Record a fresh new transaction for this account. Or, plan a future one!" 216 | msgstr "" 217 | "Zanotuj nową transakcję dla tego konta, lub zaplanuj nową na przyszłość!" 218 | 219 | #: src/view/AccountWelcomeScreen.vala:48 src/window/MainWindow.vala:105 220 | msgid "Import transactions" 221 | msgstr "Importuj transakcje" 222 | 223 | #: src/view/AccountWelcomeScreen.vala:48 224 | msgid "Import from a QIF file obtained from another application" 225 | msgstr "Importuj z pliku QIF z innej aplikacji" 226 | 227 | #: src/view/BudgetOverview.vala:93 228 | msgid "Your budget right now:" 229 | msgstr "Twój aktualny budżet:" 230 | 231 | #: src/view/BudgetOverview.vala:106 232 | msgid "Inflow:" 233 | msgstr "Przychód:" 234 | 235 | #: src/view/BudgetOverview.vala:119 236 | msgid "Outflow:" 237 | msgstr "Wydatki:" 238 | 239 | #: src/view/BudgetOverview.vala:131 240 | msgid "Remaining this month:" 241 | msgstr "Pozostałe w tym miesiącu:" 242 | 243 | #: src/view/FilterView.vala:82 244 | msgid "This month" 245 | msgstr "Ten miesiąc" 246 | 247 | #: src/view/FilterView.vala:86 248 | msgid "Last month" 249 | msgstr "Ostatni miesiąc" 250 | 251 | #: src/view/FilterView.vala:90 252 | msgid "Future" 253 | msgstr "Przyszłość" 254 | 255 | #: src/view/FilterView.vala:94 256 | msgid "Pick dates:" 257 | msgstr "Wybierz datę:" 258 | 259 | #: src/view/CategoryProperties.vala:76 260 | msgid "Edit category" 261 | msgstr "Edytuj kategorię" 262 | 263 | #: src/view/CategoryProperties.vala:81 264 | msgid "Name" 265 | msgstr "Nazwa" 266 | 267 | #: src/view/CategoryProperties.vala:91 268 | msgid "Budget" 269 | msgstr "Budżet" 270 | 271 | #: src/view/CategoryProperties.vala:98 272 | msgid "Monthly budget" 273 | msgstr "Budżet miesięczny" 274 | 275 | #: src/view/CategoryProperties.vala:106 276 | msgid "Outflow" 277 | msgstr "Wydatek" 278 | 279 | #: src/view/CategoryProperties.vala:115 280 | msgid "Inflow" 281 | msgstr "Przychód" 282 | 283 | #: src/dialog/AddAccountDialog.vala:35 284 | msgid "Add an Account" 285 | msgstr "" 286 | 287 | #: src/dialog/AddAccountDialog.vala:58 288 | msgid "Label:" 289 | msgstr "" 290 | 291 | #: src/dialog/AddAccountDialog.vala:63 292 | msgid "Eg.: account number" 293 | msgstr "Np.: numer konta" 294 | 295 | #: src/dialog/AddAccountDialog.vala:70 296 | msgid "Type:" 297 | msgstr "" 298 | 299 | #: src/dialog/AddAccountDialog.vala:78 300 | msgid "Checking" 301 | msgstr "Sprawdzam" 302 | 303 | #: src/dialog/AddAccountDialog.vala:101 304 | msgid "Balance:" 305 | msgstr "Bilans:" 306 | 307 | #: src/dialog/AddAccountDialog.vala:110 308 | msgid "Description:" 309 | msgstr "Opis:" 310 | 311 | #: src/dialog/AddAccountDialog.vala:116 312 | msgid "Optional" 313 | msgstr "Opcjonalnie" 314 | 315 | #: src/dialog/AddAccountDialog.vala:120 316 | msgid "Create Account" 317 | msgstr "Utwórz konto" 318 | 319 | #: src/dialog/AddAccountDialog.vala:151 320 | #, c-format 321 | msgid "Account %s has been created" 322 | msgstr "Konto %s zostało utworzone" 323 | 324 | #: src/dialog/AddCategoryDialog.vala:31 325 | msgid "Add a Category" 326 | msgstr "" 327 | 328 | #: src/dialog/AddCategoryDialog.vala:55 329 | msgid "Name:" 330 | msgstr "Nazwa:" 331 | 332 | #: src/dialog/AddCategoryDialog.vala:60 333 | #: src/widget/CellRendererCategoryPicker.vala:72 334 | msgid "Category name" 335 | msgstr "Nazwa kategorii" 336 | 337 | #: src/dialog/AddCategoryDialog.vala:67 338 | #, c-format 339 | msgid "Monthly Budget for %s" 340 | msgstr "Miesięczny budżet dla %s" 341 | 342 | #: src/dialog/AddCategoryDialog.vala:73 343 | msgid "Budget:" 344 | msgstr "Budżet:" 345 | 346 | #: src/dialog/AddCategoryDialog.vala:78 347 | msgid "Monthly budget for category" 348 | msgstr "Miesięczny budżet dla kategorii" 349 | 350 | #: src/dialog/AddCategoryDialog.vala:81 351 | msgid "Create Category" 352 | msgstr "Utwórz kategorię" 353 | 354 | #: src/dialog/ImportTransactionsDialog.vala:26 355 | msgid "Import transactions from file" 356 | msgstr "Importuj transakcje z pliku" 357 | 358 | #: src/dialog/ImportTransactionsDialog.vala:67 359 | #, c-format 360 | msgid "%d transactions imported in account %s" 361 | msgstr "Zaimportowano %d transakcji dla konta %s" 362 | 363 | #: src/widget/CellRendererCategoryPicker.vala:39 364 | #: src/widget/CellRendererCategoryPicker.vala:77 365 | #, c-format 366 | msgid "Apply to all %s" 367 | msgstr "Zastosuj dla wszystkich %s" 368 | 369 | #: src/widget/CellRendererCategoryPicker.vala:85 370 | msgid "Set category" 371 | msgstr "Ustaw kategorię" 372 | 373 | #: src/window/MainWindow.vala:89 374 | msgid "Export…" 375 | msgstr "Eksportuj…" 376 | 377 | #: src/window/MainWindow.vala:115 378 | msgid "Record transaction" 379 | msgstr "Zanotuj transakcję" 380 | 381 | #: src/window/MainWindow.vala:122 382 | msgid "Search transactions…" 383 | msgstr "Szukaj transakcji…" 384 | 385 | #: src/window/MainWindow.vala:290 386 | #, c-format 387 | msgid "Search in %s" 388 | msgstr "Szukaj w %s" 389 | 390 | #: src/window/MainWindow.vala:292 391 | msgid "Search uncategorized" 392 | msgstr "Szukaj nieskategoryzowanych" 393 | 394 | #: src/window/MainWindow.vala:369 395 | msgid "Transaction recorded" 396 | msgstr "Transakcja zanotowana" 397 | 398 | #: src/window/MainWindow.vala:415 399 | #, c-format 400 | msgid "Transactions in %s" 401 | msgstr "Transakcji w %s" 402 | -------------------------------------------------------------------------------- /src/Build.vala: -------------------------------------------------------------------------------- 1 | namespace Build { 2 | public const string APP_NAME = "@app_name@"; 3 | public const string DATADIR = "@datadir@"; 4 | public const string PKG_DATADIR = "@pkg_datadir@"; 5 | public const string GETTEXT_PACKAGE = "@gettext_package@"; 6 | public const string RELEASE_NAME = "@release_name@"; 7 | public const string VERSION = "@version@"; 8 | public const string VERSION_INFO = "@version_info@"; 9 | public const string GIT_BRANCH = "@git_branch@"; 10 | public const string GIT_COMMIT_HASH = "@git_commit_hash@"; 11 | } 12 | -------------------------------------------------------------------------------- /src/Envelope.vala: -------------------------------------------------------------------------------- 1 | /* Copyright 2014 Nicolas Laplante 2 | * 3 | * This file is part of envelope. 4 | * 5 | * envelope is free software: you can redistribute it 6 | * and/or modify it under the terms of the GNU General Public License as 7 | * published by the Free Software Foundation, either version 3 of the 8 | * License, or (at your option) any later version. 9 | * 10 | * envelope is distributed in the hope that it will be 11 | * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 13 | * Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with envelope. If not, see http://www.gnu.org/licenses/. 17 | */ 18 | 19 | using Envelope.Window; 20 | 21 | namespace Envelope { 22 | private static App application_instance = null; 23 | 24 | public class App : Gtk.Application { 25 | public const int TOAST_TIMEOUT = 3000; 26 | 27 | construct { 28 | application_id = Build.APP_NAME; 29 | application_instance = this; 30 | } 31 | 32 | public static new unowned Envelope.App get_default () { 33 | if (application_instance == null) { 34 | application_instance = new App (); 35 | } 36 | 37 | return application_instance; 38 | } 39 | 40 | /** 41 | * Display a toast message. The message disappears after a configured timeout. 42 | * 43 | * @param message the message to display 44 | */ 45 | public static void toast (string message) { 46 | get_default ().main_window.show_notification (message); 47 | Granite.Services.Logger.notification (message); 48 | } 49 | 50 | public MainWindow main_window { get; private set; } 51 | 52 | protected override void activate () { 53 | Granite.Services.Logger.initialize (Build.APP_NAME); 54 | Granite.Services.Logger.DisplayLevel = Build.RELEASE_NAME == "dev" ? Granite.Services.LogLevel.DEBUG : Granite.Services.LogLevel.INFO; 55 | 56 | Granite.Services.Paths.initialize (Build.APP_NAME, Build.PKG_DATADIR); 57 | 58 | info ("Revision: %s", Build.VERSION_INFO); 59 | info ("Report any issues/bugs you might find to %s", "https://github.com/cjfloss/envelope"); 60 | 61 | if (main_window == null) { 62 | main_window = new MainWindow (); 63 | main_window.set_application (this); 64 | } 65 | 66 | main_window.present (); 67 | } 68 | 69 | public string get_id () { 70 | return application_id; 71 | } 72 | 73 | public string get_name () { 74 | return Build.APP_NAME; 75 | } 76 | } 77 | } 78 | 79 | public static int main (string[] args) { 80 | var app = new Envelope.App (); 81 | return app.run (args); 82 | } 83 | -------------------------------------------------------------------------------- /src/database/Database.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009,2011 Jens Georg . 3 | * 4 | * Author: Jens Georg 5 | * 6 | * This file is part of Rygel. 7 | * 8 | * Rygel is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU Lesser General Public 10 | * License as published by the Free Software Foundation; either 11 | * version 2.1 of the License, or (at your option) any later version. 12 | * 13 | * Rygel is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Lesser General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU Lesser General Public 19 | * License along with this library; if not, write to the Free Software 20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 21 | * 22 | * Adapted to Envelope 23 | */ 24 | 25 | using Sqlite; 26 | 27 | namespace Envelope.Database { 28 | 29 | /** 30 | * Special GValue to pass to exec or exec_cursor to bind a column to 31 | * NULL 32 | */ 33 | public static GLib.Value @null () { 34 | GLib.Value v = GLib.Value (typeof (void *)); 35 | v.set_pointer (null); 36 | 37 | return v; 38 | } 39 | 40 | /** 41 | * This class is a thin wrapper around SQLite's database object. 42 | * 43 | * It adds statement preparation based on GValue and a cancellable exec 44 | * function. 45 | */ 46 | public class Database : Object, Initable { 47 | 48 | public string name { private get; construct set; default = "envelope.db"; } 49 | 50 | private Sqlite.Database db; 51 | 52 | /** 53 | * Connect to a SQLite database file 54 | * 55 | * @param name Name of the database which is used to create the file-name 56 | */ 57 | public Database (string name) throws DatabaseError, Error { 58 | Object (name : name); 59 | init (); 60 | } 61 | 62 | /** 63 | * Initialize database. Implemented for Initiable interface. 64 | * 65 | * @param cancellable a cancellable (unused) 66 | * @return true on success, false on error 67 | * @throws DatabaseError if anything goes wrong 68 | */ 69 | public bool init (Cancellable? cancellable = null) throws Error { 70 | var app_path = Granite.Services.Paths.user_data_folder; 71 | 72 | Granite.Services.Paths.ensure_directory_exists (app_path); 73 | 74 | var db_path = Path.build_filename (app_path.get_path (), this.name); 75 | var db_file = File.new_for_path (db_path); 76 | 77 | try { 78 | const int flags = Sqlite.OPEN_READWRITE | Sqlite.OPEN_CREATE; 79 | 80 | debug ("database path: " + db_file.get_path ()); 81 | 82 | Sqlite.Database.open_v2 (db_file.get_path (), out db, flags); 83 | if (db.errcode () != Sqlite.OK) { 84 | var msg = "Error while opening SQLite database %s: %s"; 85 | throw new DatabaseError.OPEN (msg, db_file.get_path (), this.db.errmsg ()); 86 | } 87 | 88 | //TODO debug executed sql queries 89 | //db.sql_executed.connect (debug_sql); 90 | } catch (DatabaseError e) { 91 | error ("SQLITE_ERROR: %d: %s\n", e.code, e.message); 92 | } 93 | 94 | return true; 95 | } 96 | 97 | /** 98 | * SQL query function. 99 | * 100 | * Use for all queries that return a result set. 101 | * 102 | * @param sql The SQL query to run. 103 | * @param arguments Values to bind in the SQL query or null. 104 | * @throws DatabaseError if the underlying SQLite operation fails. 105 | */ 106 | public Cursor exec_cursor (string sql, GLib.Value[]? arguments = null) throws DatabaseError { 107 | return new Cursor (this.db, sql, arguments); 108 | } 109 | 110 | /** 111 | * Simple SQL query execution function. 112 | * 113 | * Use for all queries that don't return anything. 114 | * 115 | * @param sql The SQL query to run. 116 | * @param arguments Values to bind in the SQL query or null. 117 | * @throws DatabaseError if the underlying SQLite operation fails. 118 | */ 119 | public void exec (string sql, GLib.Value[]? arguments = null) throws DatabaseError { 120 | if (arguments == null) { 121 | debug (sql); 122 | this.db.exec (sql); 123 | if (this.db.errcode () != Sqlite.OK) { 124 | var msg = "Failed to run query %s: %s"; 125 | throw new DatabaseError.SQLITE_ERROR (msg, sql, this.db.errmsg ()); 126 | } 127 | 128 | return; 129 | } 130 | var cursor = this.exec_cursor (sql, arguments); 131 | while (cursor.has_next ()) { 132 | cursor.next (); 133 | } 134 | } 135 | 136 | /** 137 | * Return the Last inserted row id 138 | * 139 | * Use to get database row id after an insertion 140 | */ 141 | public int64 last_insert_rowid () { 142 | return this.db.last_insert_rowid (); 143 | } 144 | 145 | /** 146 | * Execute a SQL query that returns a single number. 147 | * 148 | * @param sql The SQL query to run. 149 | * @param args Values to bind in the SQL query or null. 150 | * @return The contents of the first row's column as an int. 151 | * @throws DatabaseError if the underlying SQLite operation fails. 152 | */ 153 | public int query_value (string sql, GLib.Value[]? args = null) throws DatabaseError { 154 | var cursor = this.exec_cursor (sql, args); 155 | var statement = cursor.next (); 156 | return statement->column_int (0); 157 | } 158 | 159 | /** 160 | * Analyze triggers of database 161 | */ 162 | public void analyze () { 163 | this.db.exec ("ANALYZE;"); 164 | } 165 | 166 | /** 167 | * Start a transaction 168 | */ 169 | public void begin () throws DatabaseError { 170 | this.exec ("BEGIN;"); 171 | } 172 | 173 | /** 174 | * Commit a transaction 175 | */ 176 | public void commit () throws DatabaseError { 177 | this.exec ("COMMIT;"); 178 | } 179 | 180 | /** 181 | * Rollback a transaction 182 | */ 183 | public void rollback () { 184 | try { 185 | this.exec ("ROLLBACK;"); 186 | } catch (DatabaseError error) { 187 | critical ("Failed to roll back transaction: %s", error.message); 188 | } 189 | } 190 | 191 | /** 192 | * Check for an empty SQLite database. 193 | * @return true if the file is an empty SQLite database, false otherwise 194 | * @throws DatabaseError if the SQLite meta table does not exist which 195 | * usually indicates that the file is not a databsae 196 | */ 197 | public bool is_empty () throws DatabaseError { 198 | return this.query_value ("SELECT count(type) FROM " + "sqlite_master WHERE rowid = 1;") == 0; 199 | } 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /src/database/DatabaseCursor.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 Jens Georg . 3 | * 4 | * Author: Jens Georg 5 | * 6 | * This file is part of Rygel. 7 | * 8 | * Rygel is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU Lesser General Public 10 | * License as published by the Free Software Foundation; either 11 | * version 2.1 of the License, or (at your option) any later version. 12 | * 13 | * Rygel is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Lesser General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU Lesser General Public 19 | * License along with this library; if not, write to the Free Software 20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 21 | * 22 | * Adapted to Envelope 23 | */ 24 | 25 | using Sqlite; 26 | 27 | public class Envelope.Database.Cursor : Object { 28 | private Statement statement; 29 | private int current_state = -1; 30 | private bool dirty = true; 31 | private unowned Sqlite.Database db; 32 | 33 | /** 34 | * Prepare a SQLite statement from a SQL string 35 | * 36 | * If @arguments is non-null, it will call bind() 37 | * 38 | * @param db SQLite database this cursor belongs to 39 | * @param sql statement to execute 40 | * @param arguments array of values to bind to the SQL statement or null if 41 | * none 42 | */ 43 | public Cursor (Sqlite.Database db, string sql, GLib.Value[]? arguments) throws DatabaseError { 44 | this.db = db; 45 | 46 | this.throw_if_code_is_error (db.prepare_v2 (sql, -1, out this.statement, null)); 47 | 48 | if (arguments == null) { 49 | return; 50 | } 51 | 52 | this.bind (arguments); 53 | } 54 | 55 | /** 56 | * Bind new values to a cursor. 57 | * 58 | * The cursor will be reset. 59 | * 60 | * This function uses the type of the GValue passed in values to determine 61 | * which _bind function to use. 62 | * 63 | * Supported types are: int, long, int64, uint64, string and pointer. 64 | * Note: The only pointer supported is the null pointer as provided by 65 | * Database.@null. This is a special value to bind a column to NULL 66 | * @param arguments array of values to bind to the SQL statement or null if 67 | * none 68 | */ 69 | public void bind (GLib.Value[]? arguments) throws DatabaseError { 70 | this.statement.reset (); 71 | this.dirty = true; 72 | this.current_state = -1; 73 | 74 | for (int i = 1; i <= arguments.length; ++i) { 75 | unowned GLib.Value current_value = arguments[i - 1]; 76 | 77 | if (current_value.holds (typeof (int))) { 78 | statement.bind_int (i, current_value.get_int ()); 79 | } else if (current_value.holds (typeof (int64))) { 80 | statement.bind_int64 (i, current_value.get_int64 ()); 81 | } else if (current_value.holds (typeof (uint64))) { 82 | statement.bind_int64 (i, (int64) current_value.get_uint64 ()); 83 | } else if (current_value.holds (typeof (long))) { 84 | statement.bind_int64 (i, current_value.get_long ()); 85 | } else if (current_value.holds (typeof (uint))) { 86 | statement.bind_int64 (i, current_value.get_uint ()); 87 | } else if (current_value.holds (typeof (string))) { 88 | statement.bind_text (i, current_value.get_string ()); 89 | } else if (current_value.holds (typeof (double))) { 90 | statement.bind_double (i, current_value.get_double ()); 91 | } else if (current_value.holds (typeof (void *))) { 92 | if (current_value.peek_pointer () == null) { 93 | statement.bind_null (i); 94 | } else { 95 | assert_not_reached (); 96 | } 97 | } else { 98 | var type = current_value.type (); 99 | warning ("Unsupported type %s", type.name ()); 100 | assert_not_reached (); 101 | } 102 | 103 | if (this.db.errcode () != Sqlite.OK) { 104 | throw new DatabaseError.BIND ("Failed to bind value %d in %s: %s", i, this.statement.sql (), this.db.errmsg ()); 105 | } 106 | } 107 | } 108 | 109 | /** 110 | * Check if the cursor has more rows left 111 | * 112 | * @return true if more rows left, false otherwise 113 | */ 114 | public bool has_next () throws DatabaseError { 115 | if (this.dirty) { 116 | this.current_state = this.statement.step (); 117 | this.dirty = false; 118 | } 119 | 120 | this.throw_if_code_is_error (this.current_state); 121 | 122 | return this.current_state == Sqlite.ROW || this.current_state == -1; 123 | } 124 | 125 | /** 126 | * Get the next row of this cursor. 127 | * 128 | * This function uses pointers instead of unowned because var doesn't work 129 | * with unowned. 130 | * 131 | * @return a pointer to the current row 132 | */ 133 | public Statement* next () throws DatabaseError { 134 | this.has_next (); 135 | this.throw_if_code_is_error (this.current_state); 136 | this.dirty = true; 137 | 138 | return this.statement; 139 | } 140 | 141 | // convenience functions for "foreach" 142 | 143 | /** 144 | * Return an iterator to the cursor to use with foreach 145 | * 146 | * @return an iterator wrapping the cursor 147 | */ 148 | public Iterator iterator () { 149 | return new Iterator (this); 150 | } 151 | 152 | public class Iterator { 153 | public Cursor cursor; 154 | 155 | public Iterator (Cursor cursor) { 156 | this.cursor = cursor; 157 | } 158 | 159 | public bool next () throws DatabaseError { 160 | return this.cursor.has_next (); 161 | } 162 | 163 | public unowned Statement @get () throws DatabaseError { 164 | return this.cursor.next (); 165 | } 166 | } 167 | 168 | /** 169 | * Convert a SQLite return code to a DatabaseError 170 | */ 171 | protected void throw_if_code_is_error (int sqlite_error) throws DatabaseError { 172 | switch (sqlite_error) { 173 | case Sqlite.OK: 174 | case Sqlite.DONE: 175 | case Sqlite.ROW: 176 | return; 177 | default: 178 | throw new DatabaseError.SQLITE_ERROR ("SQLite error %d: %s", sqlite_error, this.db.errmsg ()); 179 | } 180 | } 181 | 182 | /** 183 | * Check if the last operation on the database was an error 184 | */ 185 | protected void throw_if_db_has_error () throws DatabaseError { 186 | this.throw_if_code_is_error (this.db.errcode ()); 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /src/database/DatabaseError.vala: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2009,2011 Jens Georg . 2 | * 3 | * Author: Jens Georg for Rygel 4 | * 5 | * Adapted to envelope 6 | * 7 | * envelope is free software: you can redistribute it 8 | * and/or modify it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * envelope is distributed in the hope that it will be 13 | * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 15 | * Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License along 18 | * with envelope. If not, see http://www.gnu.org/licenses/. 19 | */ 20 | 21 | namespace Envelope.Database { 22 | public errordomain DatabaseError { 23 | SQLITE_ERROR, /// Error code translated from SQLite 24 | OPEN, /// Error while opening database file 25 | PREPARE, /// Error while preparing a statement 26 | BIND, /// Error while binding values to a statement 27 | STEP, /// Error while running through a result set 28 | CONSTRAINT /// Sqlite Constraint Error 29 | } 30 | } -------------------------------------------------------------------------------- /src/database/SQLQueries.vala: -------------------------------------------------------------------------------- 1 | /* Copyright 2014 Nicolas Laplante 2 | * 2018 Cleiton Floss 3 | * 4 | * This file is part of envelope. 5 | * 6 | * envelope is free software: you can redistribute it 7 | * and/or modify it under the terms of the GNU General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * envelope is distributed in the hope that it will be 12 | * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 14 | * Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with envelope. If not, see http://www.gnu.org/licenses/. 18 | */ 19 | 20 | namespace Envelope.Database { 21 | private const string ACCOUNTS = """ 22 | CREATE TABLE IF NOT EXISTS accounts ( 23 | `id` INTEGER PRIMARY KEY AUTOINCREMENT, 24 | `number` TEXT NOT NULL UNIQUE, 25 | `description` TEXT, 26 | `balance` DOUBLE, 27 | `type` INT); 28 | """; 29 | 30 | private const string TRANSACTIONS = """ 31 | CREATE TABLE IF NOT EXISTS transactions ( 32 | `id` INTEGER PRIMARY KEY AUTOINCREMENT, 33 | `label` TEXT NOT NULL, 34 | `description` TEXT, 35 | `direction` INT NOT NULL, 36 | `amount` DOUBLE NOT NULL, 37 | `account_id` INT NOT NULL, 38 | `category_id` INT, 39 | `parent_transaction_id` INT, 40 | `date` TIMESTAMP NOT NULL, 41 | FOREIGN KEY (`parent_transaction_id`) 42 | REFERENCES `transactions`(`id`) 43 | ON UPDATE CASCADE 44 | ON DELETE CASCADE, 45 | FOREIGN KEY (`category_id`) 46 | REFERENCES `categories`(`id`) 47 | ON UPDATE CASCADE 48 | ON DELETE SET NULL, 49 | FOREIGN KEY (`account_id`) 50 | REFERENCES `accounts`(`id`) 51 | ON UPDATE CASCADE 52 | ON DELETE CASCADE); 53 | """; 54 | 55 | private const string CATEGORIES = """ 56 | CREATE TABLE IF NOT EXISTS categories ( 57 | `id` INTEGER PRIMARY KEY AUTOINCREMENT, 58 | `name` TEXT NOT NULL UNIQUE, 59 | `description` TEXT, 60 | `parent_category_id` INT, 61 | FOREIGN KEY (`parent_category_id`) 62 | REFERENCES `categories`(`id`) 63 | ON UPDATE CASCADE 64 | ON DELETE CASCADE); 65 | """; 66 | 67 | private const string MONTHLY_CATEGORIES = """ 68 | CREATE TABLE IF NOT EXISTS categories_budgets ( 69 | `category_id` INTEGER NOT NULL, 70 | `year` INTEGER NOT NULL, 71 | `month` INTEGER NOT NULL, 72 | `amount_budgeted` DOUBLE, 73 | PRIMARY KEY (`category_id`, `year`, `month`), 74 | FOREIGN KEY (`category_id`) 75 | REFERENCES `categories`(`id`) 76 | ON UPDATE CASCADE 77 | ON DELETE CASCADE 78 | ) WITHOUT ROWID; 79 | """; 80 | 81 | private const string MONTHLY_BUDGET = """ 82 | CREATE TABLE IF NOT EXISTS monthly_budgets ( 83 | `month` INTEGER NOT NULL, 84 | `year` INTEGER NOT NULL, 85 | `outflow` DOUBLE, 86 | `inflow` DOUBLE, 87 | PRIMARY KEY (`month`, `year`) 88 | ) WITHOUT ROWID; 89 | """; 90 | 91 | private const string SQL_CATEGORY_COUNT = """ 92 | SELECT COUNT(*) AS category_count 93 | FROM categories; 94 | """; 95 | 96 | private const string SQL_INSERT_CATEGORY_FOR_NAME = """ 97 | INSERT INTO `categories` 98 | (`name`) 99 | VALUES 100 | (?); 101 | """; 102 | 103 | private const string SQL_SET_CATEGORY_BUDGET = """ 104 | INSERT INTO `categories_budgets` 105 | (`category_id`, `year`, `month`, `amount_budgeted`) 106 | VALUES 107 | (?, ?, ?, ?); 108 | """; 109 | 110 | private const string SQL_UPDATE_CATEGORY_BUDGET = """ 111 | UPDATE `categories_budgets` 112 | SET `amount_budgeted` = ? 113 | WHERE `category_id` = ? 114 | AND `year` = ? 115 | AND `month` = ?; 116 | """; 117 | 118 | private const string SQL_CHECK_CATEGORY_BUDGET_SET = """ 119 | SELECT COUNT(*) AS size 120 | FROM categories_budgets 121 | WHERE category_id = ? 122 | AND year = ? 123 | AND month = ?; 124 | """; 125 | 126 | private const string SQL_DELETE_TRANSACTION = """ 127 | DELETE FROM `transactions` 128 | WHERE `id` = ?; 129 | """; 130 | 131 | private const string SQL_GET_TRANSACTION_BY_ID = """ 132 | SELECT * FROM `transactions` 133 | WHERE `id` = ?; 134 | """; 135 | 136 | private const string SQL_GET_UNCATEGORIZED_TRANSACTIONS = """ 137 | SELECT * FROM `transactions` 138 | WHERE `category_id` IS NULL; 139 | """; 140 | 141 | private const string SQL_RENAME_ACCOUNT = """ 142 | UPDATE `accounts` 143 | SET `number` = ? 144 | WHERE `id` = ?; 145 | """; 146 | 147 | private const string SQL_DELETE_ACCOUNT = """ 148 | DELETE FROM `accounts` 149 | WHERE `id` = ?; 150 | """; 151 | 152 | private const string SQL_UPDATE_ACCOUNT_BALANCE = """ 153 | UPDATE `accounts` 154 | SET `balance` = ? 155 | WHERE `id` = ?; 156 | """; 157 | 158 | private const string SQL_LOAD_ACCOUNT_TRANSACTIONS = """ 159 | SELECT * FROM `transactions` 160 | WHERE `account_id` = ? 161 | ORDER BY `date` DESC; 162 | """; 163 | 164 | private const string SQL_DELETE_ACCOUNT_TRANSACTIONS = """ 165 | DELETE FROM `transactions` WHERE `account_id` = ?;"""; 166 | 167 | private const string SQL_GET_UNIQUE_PAYEES = """ 168 | SELECT `label`, COUNT(`label`) as `number` 169 | FROM `transactions` 170 | GROUP BY `label` 171 | ORDER BY `number` 172 | DESC, `label` ASC; 173 | """; 174 | 175 | private const string SQL_LOAD_CATEGORIES = """ 176 | SELECT `c`.*, `cb`.`year`, `cb`.`month`, `cb`.`amount_budgeted` 177 | FROM `categories` `c` 178 | LEFT JOIN `categories_budgets` `cb` ON `cb`.`category_id` = `c`.`id` 179 | AND `cb`.`year` = strftime('%Y', 'now') 180 | AND `cb`.`month` = strftime('%m', 'now') 181 | ORDER BY `c`.`name` ASC; 182 | """; 183 | 184 | private const string SQL_LOAD_CHILD_CATEGORIES = """ 185 | SELECT * FROM `categories` WHERE `parent_category_id` = ? ORDER BY `name` ASC;"""; 186 | 187 | private const string SQL_DELETE_CATEOGRY = """ 188 | DELETE FROM `categories` WHERE `id` = ?;"""; 189 | 190 | private const string SQL_UPDATE_CATEGORY = """ 191 | UPDATE `categories` SET `name` = ?, `description` = ?, `parent_category_id` = ? WHERE `id` = ?;"""; 192 | 193 | private const string SQL_CATEGORIZE_ALL_FOR_PAYEE = """ 194 | UPDATE `transactions` SET `category_id` = ? WHERE `label` = ?;"""; 195 | 196 | private const string SQL_LOAD_CURRENT_TRANSACTIONS = """ 197 | SELECT * FROM transactions 198 | WHERE date(date, 'unixepoch') 199 | BETWEEN date('now', 'start of month') 200 | AND date('now', 'start of month', '+1 month', '-1 days'); 201 | """; 202 | 203 | private const string SQL_LOAD_TRANSACTIONS_FOR_MONTH = """ 204 | SELECT t.*, c.*, cb.* FROM transactions t 205 | LEFT JOIN categories c 206 | ON c.id = t.category_id 207 | LEFT JOIN categories_budgets cb 208 | ON cb.category_id = t.category_id AND cb.year = ? and cb.month = ? 209 | WHERE date(t.date, 'unixepoch') 210 | BETWEEN date(?, 'start of month') 211 | AND date(?, 'start of month', '+1 month', '-1 days') 212 | ORDER BY t.date DESC; 213 | """; 214 | 215 | private const string SQL_LOAD_CURRENT_TRANSACTIONS_FOR_CATEGORY = """ 216 | SELECT * FROM transactions 217 | WHERE date(date, 'unixepoch') 218 | BETWEEN date('now', 'start of month') 219 | AND date('now', 'start of month', '+1 month', '-1 days') 220 | AND category_id = ?; 221 | """; 222 | 223 | private const string SQL_LOAD_CURRENT_UNCATEGORIZED_TRANSACTIONS = """ 224 | SELECT * FROM transactions 225 | WHERE date (date, 'unixepoch') 226 | BETWEEN date('now', 'start of month') 227 | AND date('now', 'start of month', '+1 month', '-1 days') 228 | AND category_id IS NULL; 229 | """; 230 | 231 | private const string SQL_INSERT_CATEGORY = """ 232 | INSERT INTO `categories` 233 | (`name`, `description`, `parent_category_id`) 234 | VALUES 235 | (?, ?, ?); 236 | """; 237 | 238 | private const string SQL_UPDATE_TRANSACTION = """ 239 | UPDATE `transactions` SET 240 | label = ?, 241 | description = ?, 242 | direction = ?, 243 | amount = ?, 244 | account_id = ?, 245 | category_id = ?, 246 | parent_transaction_id = ?, 247 | date = ? 248 | WHERE id = ?; 249 | """; 250 | 251 | private const string SQL_INSERT_TRANSACTION = """ 252 | INSERT INTO `transactions` 253 | (`label`, `description`, `amount`, `direction`, `account_id`, `parent_transaction_id`, `date`, `category_id`) 254 | VALUES 255 | (?, ?, ?, ?, ?, ?, ?, ?); 256 | """; 257 | 258 | private const string SQL_LOAD_ACCOUNT_BY_ID = """ 259 | SELECT * FROM `accounts` WHERE `id` = ?;"""; 260 | 261 | private const string SQL_LOAD_ALL_ACCOUNTS = """ 262 | SELECT * FROM `accounts` ORDER BY `number`;"""; 263 | 264 | private const string SQL_INSERT_ACCOUNT = """ 265 | INSERT INTO `accounts` 266 | (`number`, `description`, `balance`, `type`) 267 | VALUES 268 | (?, ?, ?, ?); 269 | """; 270 | } 271 | -------------------------------------------------------------------------------- /src/dialog/AddAccountDialog.vala: -------------------------------------------------------------------------------- 1 | /* Copyright 2014 Nicolas Laplante 2 | * 3 | * This file is part of envelope. 4 | * 5 | * envelope is free software: you can redistribute it 6 | * and/or modify it under the terms of the GNU General Public License as 7 | * published by the Free Software Foundation, either version 3 of the 8 | * License, or (at your option) any later version. 9 | * 10 | * envelope is distributed in the hope that it will be 11 | * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 13 | * Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with envelope. If not, see http://www.gnu.org/licenses/. 17 | */ 18 | 19 | using Envelope.Service; 20 | 21 | namespace Envelope.Dialog { 22 | public class AddAccountDialog : Gtk.Dialog { 23 | private Account.Type current_account_type = Account.Type.CHECKING; 24 | 25 | private Gtk.Entry number_entry; 26 | private Gtk.Entry desc_entry; 27 | private Gtk.Entry balance_entry; 28 | private Gtk.ComboBox type_choice; 29 | private Gtk.Button create_button; 30 | private Gtk.Button cancel_button; 31 | 32 | public AddAccountDialog (Gtk.Window parent) { 33 | Object ( 34 | transient_for: parent, 35 | title: _("Add an Account"), 36 | deletable: false, 37 | modal: true, 38 | resizable: false, 39 | width_request: 300, 40 | window_position: Gtk.WindowPosition.CENTER_ON_PARENT 41 | ); 42 | } 43 | 44 | construct { 45 | var grid = new Gtk.Grid (); 46 | get_content_area ().add (grid); 47 | 48 | grid.margin_start = 12; 49 | grid.margin_end = 12; 50 | grid.margin_top = 20; 51 | grid.row_spacing = 12; 52 | grid.column_spacing = 12; 53 | 54 | grid.orientation = Gtk.Orientation.VERTICAL; 55 | grid.valign = Gtk.Align.CENTER; 56 | grid.vexpand = true; 57 | 58 | var number_label = new Gtk.Label (_("Label:")); 59 | number_label.xalign = 1f; 60 | grid.attach (number_label, 0, 1, 1, 1); 61 | 62 | number_entry = new Gtk.Entry (); 63 | number_entry.placeholder_text = _("Eg.: account number"); 64 | number_entry.expand = true; 65 | number_entry.key_release_event.connect (() => { 66 | number_entry.get_text () == "" ? create_button.sensitive = false : create_button.sensitive = true; 67 | }); 68 | grid.attach (number_entry, 1, 1, 1, 1); 69 | 70 | var type_label = new Gtk.Label (_("Type:")); 71 | type_label.xalign = 1f; 72 | grid.attach (type_label, 0, 2, 1, 1); 73 | 74 | var type_list_store = new Gtk.ListStore (2, typeof (string), typeof (int)); 75 | Gtk.TreeIter type_list_store_iter; 76 | 77 | type_list_store.append (out type_list_store_iter); 78 | type_list_store.set (type_list_store_iter, 0, _("Checking"), 1, Account.Type.CHECKING); 79 | type_list_store.append (out type_list_store_iter); 80 | type_list_store.set (type_list_store_iter, 0, _("Savings"), 1, Account.Type.SAVINGS); 81 | 82 | type_choice = new Gtk.ComboBox.with_model (type_list_store); 83 | type_choice.expand = true; 84 | grid.attach (type_choice, 1, 2, 1, 1); 85 | 86 | var type_renderer = new Gtk.CellRendererText (); 87 | type_choice.pack_start (type_renderer, true); 88 | type_choice.add_attribute (type_renderer, "text", 0); 89 | 90 | type_choice.active = 0; 91 | 92 | type_choice.changed.connect (() => { 93 | type_choice.get_active_iter (out type_list_store_iter); 94 | 95 | int account_type; 96 | type_list_store.@get (type_list_store_iter, 1, out account_type, -1); 97 | 98 | current_account_type = Account.Type.from_int (account_type); 99 | }); 100 | 101 | var balance_label = new Gtk.Label (_("Balance:")); 102 | balance_label.xalign = 1f; 103 | grid.attach (balance_label, 0, 3, 1, 1); 104 | 105 | balance_entry = new Gtk.Entry (); 106 | balance_entry.expand = true; 107 | balance_entry.placeholder_text = Envelope.Util.String.format_currency (0d); 108 | grid.attach (balance_entry, 1, 3, 1, 1); 109 | 110 | var desc_label = new Gtk.Label (_("Description:")); 111 | desc_label.xalign = 1f; 112 | grid.attach (desc_label, 0, 4, 1, 1); 113 | 114 | desc_entry = new Gtk.Entry (); 115 | desc_entry.expand = true; 116 | desc_entry.placeholder_text = _("Optional"); 117 | grid.attach (desc_entry, 1, 4, 1, 1); 118 | grid.show_all (); 119 | 120 | create_button = new Gtk.Button.with_label (_("Create Account")); 121 | cancel_button = new Gtk.Button.with_label (_("Cancel")); 122 | 123 | create_button.sensitive = false; 124 | create_button.get_style_context ().add_class (Gtk.STYLE_CLASS_SUGGESTED_ACTION); 125 | 126 | cancel_button.clicked.connect (() => { 127 | this.destroy (); 128 | }); 129 | 130 | create_button.clicked.connect (() => { 131 | this.create_account (); 132 | this.destroy (); 133 | }); 134 | 135 | var action_area = (Gtk.Container) get_action_area (); 136 | action_area.margin = 6; 137 | action_area.margin_top = 14; 138 | action_area.add (cancel_button); 139 | action_area.add (create_button); 140 | action_area.show_all (); 141 | } 142 | 143 | [Version (deprecated = true, deprecated_since = "", replacement = "AccountManager.accout_created")] 144 | public signal void account_created (Account account); 145 | 146 | private void create_account () { 147 | try { 148 | var account = AccountManager.get_default ().create_account (number_entry.text, desc_entry.text, double.parse (balance_entry.text), current_account_type); 149 | 150 | // show notification 151 | Envelope.App.toast (_("Account %s has been created").printf (account.number)); 152 | } catch (ServiceError err) { 153 | error ("error while creating account (%s)".printf (err.message)); 154 | } catch (AccountError err) { 155 | error ("error while creating account (%s)".printf (err.message)); 156 | } 157 | } 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /src/dialog/AddCategoryDialog.vala: -------------------------------------------------------------------------------- 1 | /* Copyright 2014 Nicolas Laplante 2 | * 3 | * This file is part of envelope. 4 | * 5 | * envelope is free software: you can redistribute it 6 | * and/or modify it under the terms of the GNU General Public License as 7 | * published by the Free Software Foundation, either version 3 of the 8 | * License, or (at your option) any later version. 9 | * 10 | * envelope is distributed in the hope that it will be 11 | * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 13 | * Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with envelope. If not, see http://www.gnu.org/licenses/. 17 | */ 18 | 19 | using Envelope.Service; 20 | using Envelope.Util.String; 21 | 22 | namespace Envelope.Dialog { 23 | public class AddCategoryDialog : Gtk.Dialog { 24 | private Gtk.Entry name_entry; 25 | private Gtk.Entry amount_entry; 26 | private Gtk.Button create_button; 27 | private Gtk.Button cancel_button; 28 | 29 | public AddCategoryDialog (Gtk.Window parent) { 30 | Object ( 31 | title: _("Add a Category"), 32 | transient_for: parent, 33 | deletable: false, 34 | modal: true, 35 | resizable: false, 36 | width_request: 300, 37 | window_position: Gtk.WindowPosition.CENTER_ON_PARENT 38 | ); 39 | } 40 | 41 | construct { 42 | var grid = new Gtk.Grid (); 43 | get_content_area ().add (grid); 44 | 45 | grid.margin_start = 12; 46 | grid.margin_end = 12; 47 | grid.margin_top = 20; 48 | grid.row_spacing = 12; 49 | grid.column_spacing = 12; 50 | 51 | grid.orientation = Gtk.Orientation.VERTICAL; 52 | grid.valign = Gtk.Align.CENTER; 53 | grid.vexpand = true; 54 | 55 | var name_label = new Gtk.Label (_("Name:")); 56 | name_label.xalign = 1.0f; 57 | grid.attach (name_label, 1, 1, 1, 1); 58 | 59 | name_entry = new Gtk.Entry (); 60 | name_entry.placeholder_text = _("Category name"); 61 | name_entry.expand = true; 62 | name_entry.key_release_event.connect (() => { 63 | if (name_entry.get_text () == "") { 64 | create_button.sensitive = false; 65 | } else { 66 | create_button.sensitive = true; 67 | amount_entry.placeholder_text = _("Monthly Budget for %s").printf (name_entry.get_text ()); 68 | } 69 | }); 70 | 71 | grid.attach (name_entry, 2, 1, 2, 1); 72 | 73 | var amount_label = new Gtk.Label (_("Budget:")); 74 | amount_label.xalign = 1.0f; 75 | grid.attach (amount_label, 1, 2, 1, 1); 76 | 77 | amount_entry = new Gtk.Entry (); 78 | amount_entry.placeholder_text = _("Monthly budget for category"); 79 | grid.attach (amount_entry, 2, 2, 2, 1); 80 | 81 | create_button = new Gtk.Button.with_label (_("Create Category")); 82 | create_button.sensitive = false; 83 | create_button.get_style_context ().add_class (Gtk.STYLE_CLASS_SUGGESTED_ACTION); 84 | create_button.clicked.connect (() => { 85 | this.create_category (); 86 | this.destroy (); 87 | }); 88 | 89 | cancel_button = new Gtk.Button.with_label (_("Cancel")); 90 | cancel_button.clicked.connect (() => { 91 | this.destroy (); 92 | }); 93 | 94 | var action_area = (Gtk.Container) get_action_area (); 95 | action_area.margin = 6; 96 | action_area.margin_top = 14; 97 | action_area.add (cancel_button); 98 | action_area.add (create_button); 99 | action_area.show_all (); 100 | 101 | grid.show_all (); 102 | } 103 | 104 | private void create_category () { 105 | try { 106 | BudgetManager.get_default ().create_category (name_entry.text.strip (), parse_currency (amount_entry.text.strip ())); 107 | } catch (ParseError err) { 108 | error ("could not create category %s (%s)", name_entry.text, err.message); 109 | } catch (ServiceError err) { 110 | error ("could not create category %s (%s)", name_entry.text, err.message); 111 | } 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/dialog/ImportTransactionsDialog.vala: -------------------------------------------------------------------------------- 1 | /* Copyright 2014 Nicolas Laplante 2 | * 3 | * This file is part of envelope. 4 | * 5 | * envelope is free software: you can redistribute it 6 | * and/or modify it under the terms of the GNU General Public License as 7 | * published by the Free Software Foundation, either version 3 of the 8 | * License, or (at your option) any later version. 9 | * 10 | * envelope is distributed in the hope that it will be 11 | * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 13 | * Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with envelope. If not, see http://www.gnu.org/licenses/. 17 | */ 18 | 19 | using Envelope.Service; 20 | using Envelope.View; 21 | 22 | namespace Envelope.Dialog { 23 | public class ImportTransactionsDialog : Gtk.FileChooserDialog { 24 | public ImportTransactionsDialog () { 25 | Object ( 26 | title: _("Import transactions from file"), 27 | transient_for: (Gtk.Window) Envelope.App.get_default ().main_window.get_toplevel (), 28 | action: Gtk.FileChooserAction.OPEN, 29 | select_multiple: false, 30 | create_folders: false 31 | ); 32 | } 33 | 34 | construct { 35 | add_button ("_Cancel", Gtk.ResponseType.CANCEL); 36 | add_button ("_Open", Gtk.ResponseType.ACCEPT); 37 | 38 | debug ("pointing file chooser to path: %s".printf (Granite.Services.Paths.home_folder.get_path ())); 39 | 40 | try { 41 | set_current_folder_file (Granite.Services.Paths.home_folder); 42 | } catch (Error err) { 43 | warning ("could not point chooser to home folder (%s)".printf (err.message)); 44 | } 45 | 46 | var filter = new Gtk.FileFilter (); 47 | filter.add_pattern ("*.qif"); 48 | 49 | set_filter (filter); 50 | } 51 | 52 | // call this instead of run () 53 | public void execute () { 54 | var response = run (); 55 | debug ("import dialog returned with %d", response); 56 | 57 | switch (response) { 58 | case Gtk.ResponseType.ACCEPT: 59 | case Gtk.ResponseType.OK: 60 | close (); 61 | 62 | try { 63 | var account_ref = Sidebar.get_default ().selected_account; 64 | 65 | int size = AccountManager.get_default ().import_transactions_from_file (ref account_ref, get_file ()); 66 | 67 | Envelope.App.toast (_("%d transactions imported in account %s").printf (size, account_ref.number)); 68 | 69 | // refresh search autocompletion 70 | PayeeStore.get_default ().reload (); 71 | } catch (ServiceError err) { 72 | error (err.message); 73 | } catch (ImporterError err) { 74 | error (err.message); 75 | } 76 | break; 77 | case Gtk.ResponseType.CANCEL: 78 | case Gtk.ResponseType.CLOSE: 79 | case Gtk.ResponseType.DELETE_EVENT: 80 | close (); 81 | break; 82 | default: 83 | assert_not_reached (); 84 | } 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/meson.build: -------------------------------------------------------------------------------- 1 | app_name = meson.project_name () 2 | version = meson.project_version () 3 | release_name = 'release' 4 | 5 | prefix = get_option ('prefix') 6 | datadir = join_paths (prefix, get_option ('datadir')) 7 | pkg_datadir = join_paths (datadir, app_name) 8 | if find_program ('git', required : false).found () 9 | GIT_BRANCH = run_command ('git', 'rev-parse', '--abbrev-ref', 'HEAD') 10 | GIT_COMMIT_HASH = run_command ('git','log', '-1', '--format=%h') 11 | git_branch = GIT_BRANCH.stdout ().strip () 12 | git_commit_hash = GIT_COMMIT_HASH.stdout ().strip () 13 | version_info = 'git-' + git_branch + '-' + git_commit_hash 14 | else 15 | git_branch = '' 16 | git_commit_hash = '' 17 | version_info = release_name 18 | endif 19 | 20 | config = configuration_data () 21 | config.set ('app_name', app_name) 22 | config.set ('datadir', datadir) 23 | config.set ('pkg_datadir', pkg_datadir) 24 | config.set ('gettext_package', app_name) 25 | config.set ('release_name', release_name) 26 | config.set ('version', version) 27 | config.set ('version_info', version_info) 28 | config.set ('git_branch', git_branch) 29 | config.set ('git_commit_hash', git_commit_hash) 30 | 31 | build_config = configure_file ( 32 | input: 'Build.vala', 33 | output: 'Build.vala', 34 | configuration: config 35 | ) 36 | 37 | executable ( 38 | meson.project_name (), 39 | 'vapi/monetary.vapi', 40 | 41 | 'util/String.vala', 42 | 'util/Date.vala', 43 | 44 | 'model/Account.vala', 45 | 'model/Budget.vala', 46 | 'model/Transaction.vala', 47 | 'model/Category.vala', 48 | 'model/MonthlyCategory.vala', 49 | 'model/Payee.vala', 50 | 51 | 'database/SQLQueries.vala', 52 | 'database/DatabaseError.vala', 53 | 'database/Database.vala', 54 | 'database/DatabaseManager.vala', 55 | 'database/DatabaseCursor.vala', 56 | 57 | 'service/Error.vala', 58 | 'service/AccountManager.vala', 59 | 'service/BudgetManager.vala', 60 | 'service/Importer.vala', 61 | 'service/QIFImporter.vala', 62 | 'service/PayeeStore.vala', 63 | 'service/CategoryStore.vala', 64 | 'service/settings/SavedState.vala', 65 | 66 | 'view/TransactionView.vala', 67 | 'view/Sidebar.vala', 68 | 'view/WelcomeScreen.vala', 69 | 'view/AccountWelcomeScreen.vala', 70 | 'view/BudgetOverview.vala', 71 | 'view/FilterView.vala', 72 | 'view/CategoryProperties.vala', 73 | 74 | 'dialog/AddAccountDialog.vala', 75 | 'dialog/AddCategoryDialog.vala', 76 | 'dialog/ImportTransactionsDialog.vala', 77 | 78 | 'widget/AbstractPopoverCellRenderer.vala', 79 | 'widget/CellRendererDatePicker.vala', 80 | 'widget/CellRendererTextCompletion.vala', 81 | 'widget/CellRendererCategoryPicker.vala', 82 | 'widget/CellRendererUpdatable.vala', 83 | 'widget/CellRendererPopoverContainer.vala', 84 | 85 | 'window/MainWindow.vala', 86 | 'Envelope.vala', 87 | build_config, 88 | 89 | dependencies: [ 90 | dependency ('glib-2.0', version:'>= 2.30'), 91 | dependency ('gtk+-3.0', version:'>= 3.14'), 92 | dependency ('gee-0.8'), 93 | dependency ('granite', version: '>= 0.3.0'), 94 | dependency ('sqlite3'), 95 | dependency ('gio-2.0'), 96 | meson.get_compiler ('c').find_library ('m', required: true) 97 | ], 98 | install: true 99 | ) 100 | -------------------------------------------------------------------------------- /src/model/Account.vala: -------------------------------------------------------------------------------- 1 | /* Copyright 2014 Nicolas Laplante 2 | * 3 | * This file is part of envelope. 4 | * 5 | * envelope is free software: you can redistribute it 6 | * and/or modify it under the terms of the GNU General Public License as 7 | * published by the Free Software Foundation, either version 3 of the 8 | * License, or (at your option) any later version. 9 | * 10 | * envelope is distributed in the hope that it will be 11 | * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 13 | * Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with envelope. If not, see http://www.gnu.org/licenses/. 17 | */ 18 | 19 | using Gee; 20 | 21 | namespace Envelope { 22 | public class Account : Object, Comparable { 23 | public enum Type { 24 | CHECKING, 25 | SAVINGS; 26 | 27 | public static Type from_int (int type_int) { 28 | switch (type_int) { 29 | case 0: 30 | return CHECKING; 31 | case 1: 32 | return SAVINGS; 33 | default: 34 | assert_not_reached (); 35 | } 36 | } 37 | } 38 | 39 | public Gee.List transactions { get; set; } 40 | public string number { get; set; } 41 | public string description { get; set; } 42 | public Type account_type { get; set; default = Type.CHECKING; } 43 | public double balance { get; set; default = 0d; } 44 | public int @id { get; set; } 45 | 46 | public bool has_transactions { get { 47 | return transactions != null && transactions.size > 0; 48 | } } 49 | 50 | public Account () { 51 | transactions = new Gee.ArrayList (); 52 | } 53 | 54 | public Account.with_number (string number) { 55 | this (); 56 | this.number = number; 57 | } 58 | 59 | public Account.from_transaction_list (ref ArrayList transactions) { 60 | this (); 61 | this.transactions = transactions; 62 | } 63 | 64 | public void record_transaction (Transaction transaction) { 65 | switch (transaction.direction) { 66 | case Transaction.Direction.INCOMING: 67 | balance += transaction.amount; 68 | break; 69 | case Transaction.Direction.OUTGOING: 70 | balance -= transaction.amount; 71 | break; 72 | } 73 | 74 | transactions.add (transaction); 75 | } 76 | 77 | public void delete_transaction (Transaction transaction) { 78 | switch (transaction.direction) { 79 | case Transaction.Direction.INCOMING: 80 | balance += transaction.amount; 81 | break; 82 | case Transaction.Direction.OUTGOING: 83 | balance -= transaction.amount; 84 | break; 85 | } 86 | 87 | // TODO remove from list 88 | 89 | } 90 | 91 | public int compare_to (Account account) { 92 | return 1; 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/model/Budget.vala: -------------------------------------------------------------------------------- 1 | /* Copyright 2014 Nicolas Laplante 2 | * 3 | * This file is part of envelope. 4 | * 5 | * envelope is free software: you can redistribute it 6 | * and/or modify it under the terms of the GNU General Public License as 7 | * published by the Free Software Foundation, either version 3 of the 8 | * License, or (at your option) any later version. 9 | * 10 | * envelope is distributed in the hope that it will be 11 | * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 13 | * Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with envelope. If not, see http://www.gnu.org/licenses/. 17 | */ 18 | 19 | using Gee; 20 | using Envelope.Service; 21 | 22 | namespace Envelope { 23 | public class Budget : Object { 24 | private static Budget budget_instance = null; 25 | 26 | public static new Budget get_default () { 27 | if (budget_instance == null) { 28 | budget_instance = new Budget (); 29 | } 30 | 31 | return budget_instance; 32 | } 33 | 34 | public ArrayList categories { get; set; } 35 | 36 | public BudgetState current_state { get; set; } 37 | 38 | private Budget () { 39 | Object (); 40 | connect_signals (); 41 | } 42 | 43 | private void connect_signals () { 44 | /* 45 | connect signals in order to update current_state whenever a transaction/account 46 | is added/updated/removed 47 | */ 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/model/Category.vala: -------------------------------------------------------------------------------- 1 | /* Copyright 2014 Nicolas Laplante 2 | * 3 | * This file is part of envelope. 4 | * 5 | * envelope is free software: you can redistribute it 6 | * and/or modify it under the terms of the GNU General Public License as 7 | * published by the Free Software Foundation, either version 3 of the 8 | * License, or (at your option) any later version. 9 | * 10 | * envelope is distributed in the hope that it will be 11 | * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 13 | * Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with envelope. If not, see http://www.gnu.org/licenses/. 17 | */ 18 | 19 | using Gee; 20 | 21 | namespace Envelope { 22 | public class Category : Object, Comparable { 23 | public string name { get; set; } 24 | public string? description { get; set; } 25 | public int? @id { get; set; } 26 | public Category? parent { get; set; default = null; } 27 | public ArrayList transactions { get; set; } 28 | 29 | public Category () { 30 | Object (); 31 | } 32 | 33 | public int compare_to (Category category) { 34 | if (name == category.name) { 35 | return 0; 36 | } 37 | 38 | if (name > category.name) { 39 | return 1; 40 | } 41 | 42 | return -1; 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/model/MonthlyCategory.vala: -------------------------------------------------------------------------------- 1 | /* Copyright 2014 Nicolas Laplante 2 | * 3 | * This file is part of envelope. 4 | * 5 | * envelope is free software: you can redistribute it 6 | * and/or modify it under the terms of the GNU General Public License as 7 | * published by the Free Software Foundation, either version 3 of the 8 | * License, or (at your option) any later version. 9 | * 10 | * envelope is distributed in the hope that it will be 11 | * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 13 | * Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with envelope. If not, see http://www.gnu.org/licenses/. 17 | */ 18 | 19 | 20 | namespace Envelope { 21 | 22 | /** 23 | * A category subclass which has a budgeted amount for a specific month in history 24 | */ 25 | public class MonthlyCategory : Category { 26 | // year and month for this category's budget 27 | public uint month { get; set; } 28 | public uint year { get; set; } 29 | 30 | // budgeted amount for the month 31 | public double amount_budgeted { get; set; default = 0d; } 32 | 33 | /** 34 | * Creates a new MonthlyCategory for the current month 35 | */ 36 | public MonthlyCategory () { 37 | Object (); 38 | 39 | uint o_month, o_year; 40 | Envelope.Util.Date.get_year_month (out o_month, out o_year); 41 | 42 | month = o_month; 43 | year = o_year; 44 | } 45 | 46 | /** 47 | * Creates a new MonthlyCategory for the specified year and month 48 | */ 49 | public MonthlyCategory.for_month (uint year, uint month) { 50 | Object (); 51 | 52 | this.month = month; 53 | this.year = year; 54 | } 55 | 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/model/Payee.vala: -------------------------------------------------------------------------------- 1 | /* Copyright 2014 Nicolas Laplante 2 | * 3 | * This file is part of envelope. 4 | * 5 | * envelope is free software: you can redistribute it 6 | * and/or modify it under the terms of the GNU General Public License as 7 | * published by the Free Software Foundation, either version 3 of the 8 | * License, or (at your option) any later version. 9 | * 10 | * envelope is distributed in the hope that it will be 11 | * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 13 | * Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with envelope. If not, see http://www.gnu.org/licenses/. 17 | */ 18 | 19 | using Gee; 20 | 21 | namespace Envelope { 22 | public class Payee : Object, Comparable { 23 | public string label { get; private set; } 24 | public int occurences { get; private set; } 25 | 26 | public Payee (string label, int occurences) { 27 | Object (); 28 | this.label = label; 29 | this.occurences = occurences; 30 | } 31 | 32 | public int compare_to (Payee payee) { 33 | if (occurences > payee.occurences) { 34 | return 1; 35 | } 36 | 37 | if (occurences == payee.occurences) { 38 | return 0; 39 | } 40 | 41 | return -1; 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/model/Transaction.vala: -------------------------------------------------------------------------------- 1 | /* Copyright 2014 Nicolas Laplante 2 | * 3 | * This file is part of envelope. 4 | * 5 | * envelope is free software: you can redistribute it 6 | * and/or modify it under the terms of the GNU General Public License as 7 | * published by the Free Software Foundation, either version 3 of the 8 | * License, or (at your option) any later version. 9 | * 10 | * envelope is distributed in the hope that it will be 11 | * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 13 | * Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with envelope. If not, see http://www.gnu.org/licenses/. 17 | */ 18 | 19 | using Gee; 20 | 21 | namespace Envelope { 22 | public class Transaction : Object, Comparable { 23 | public enum Direction { 24 | INCOMING, 25 | OUTGOING; 26 | 27 | public static Direction from_int (int direction) { 28 | switch (direction) { 29 | case 0: 30 | return Direction.INCOMING; 31 | case 1: 32 | return Direction.OUTGOING; 33 | default: 34 | assert_not_reached (); 35 | } 36 | } 37 | } 38 | 39 | public string label { get; set; } 40 | public string description { get; set; } 41 | public Transaction.Direction direction { get; set; } 42 | public double amount { get; set; } 43 | public Account account { get; set; } 44 | public DateTime date { get; set; } 45 | public int? @id { get; set; } 46 | public Transaction? parent { get; set; } 47 | public Category? category { get; set; } 48 | 49 | public Transaction () { 50 | amount = 0d; 51 | label = "Untitled"; 52 | description = ""; 53 | account = null; 54 | } 55 | 56 | public Transaction.from_parent (ref Transaction transaction) { 57 | this (); 58 | parent = transaction; 59 | } 60 | 61 | // compare by date 62 | public int compare_to (Transaction transaction) { 63 | return -date.compare (transaction.date); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/service/CategoryStore.vala: -------------------------------------------------------------------------------- 1 | /* Copyright 2014 Nicolas Laplante 2 | * 3 | * This file is part of envelope. 4 | * 5 | * envelope is free software: you can redistribute it 6 | * and/or modify it under the terms of the GNU General Public License as 7 | * published by the Free Software Foundation, either version 3 of the 8 | * License, or (at your option) any later version. 9 | * 10 | * envelope is distributed in the hope that it will be 11 | * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 13 | * Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with envelope. If not, see http://www.gnu.org/licenses/. 17 | */ 18 | 19 | using Envelope.Service; 20 | using Gee; 21 | 22 | namespace Envelope.Service { 23 | private static CategoryStore category_store_instance = null; 24 | 25 | public class CategoryStore : Gtk.ListStore { 26 | public static new unowned CategoryStore get_default () { 27 | if (category_store_instance == null) { 28 | category_store_instance = new CategoryStore (); 29 | } 30 | 31 | return category_store_instance; 32 | } 33 | 34 | public enum Column { 35 | LABEL, 36 | CATEGORY 37 | } 38 | 39 | /** 40 | * Reload the store 41 | */ 42 | public void reload () { 43 | try { 44 | Collection categories = BudgetManager.get_default ().get_categories (); 45 | 46 | foreach (MonthlyCategory category in categories) { 47 | 48 | Gtk.TreeIter iter; 49 | append (out iter); 50 | 51 | @set (iter, Column.LABEL, category.name, Column.CATEGORY, category, -1); 52 | } 53 | } catch (ServiceError err) { 54 | warning ("could not load categories; autocompletion won't work (%s)", err.message); 55 | } 56 | } 57 | 58 | /** 59 | * Get a category instance from its name 60 | * 61 | * @param name the name of the category to lookup 62 | * @return the category instance, or null if not found 63 | */ 64 | public Category? get_category_by_name (string name) { 65 | Category? category = null; 66 | 67 | @foreach ((model, path, iter) => { 68 | Category fe_category; 69 | model.@get (iter, Column.CATEGORY, out fe_category, -1); 70 | 71 | if (fe_category != null && fe_category.name.up () == name.strip ().up ()) { 72 | category = fe_category; 73 | } 74 | 75 | return category != null; 76 | }); 77 | 78 | return category; 79 | } 80 | 81 | /** 82 | * Get a category instance from its id 83 | * 84 | * @param id the id of the category to lookup 85 | * @return the category instance, or null if not found 86 | */ 87 | public Category get_category_by_id (int id) { 88 | Category? category = null; 89 | 90 | @foreach ((model, path, iter) => { 91 | Category fe_category; 92 | 93 | model.@get (iter, Column.CATEGORY, out fe_category, -1); 94 | 95 | if (fe_category.@id == id) { 96 | category = fe_category; 97 | } 98 | 99 | return category != null; 100 | }); 101 | 102 | return category; 103 | } 104 | 105 | private CategoryStore () { 106 | Object (); 107 | 108 | build_store (); 109 | connect_signals (); 110 | } 111 | 112 | private void build_store () { 113 | set_column_types ({typeof (string), typeof (MonthlyCategory)}); 114 | reload (); 115 | } 116 | 117 | private void connect_signals () { 118 | var budget_manager = BudgetManager.get_default (); 119 | 120 | budget_manager.category_added.connect ((category) => { 121 | debug ("category added; reloading"); 122 | reload (); 123 | }); 124 | 125 | budget_manager.category_deleted.connect ((category) => { 126 | debug ("category deleted; reloading"); 127 | reload (); 128 | }); 129 | 130 | budget_manager.category_renamed.connect ((category) => { 131 | debug ("category renamed; reloading"); 132 | reload (); 133 | }); 134 | } 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /src/service/Error.vala: -------------------------------------------------------------------------------- 1 | /* Copyright 2014 Nicolas Laplante 2 | * 3 | * This file is part of envelope. 4 | * 5 | * envelope is free software: you can redistribute it 6 | * and/or modify it under the terms of the GNU General Public License as 7 | * published by the Free Software Foundation, either version 3 of the 8 | * License, or (at your option) any later version. 9 | * 10 | * envelope is distributed in the hope that it will be 11 | * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 13 | * Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with envelope. If not, see http://www.gnu.org/licenses/. 17 | */ 18 | 19 | namespace Envelope.Service { 20 | public errordomain ServiceError { 21 | DATABASE_ERROR, 22 | IMPORT_ERROR, 23 | ENOENT, 24 | IO 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/service/Importer.vala: -------------------------------------------------------------------------------- 1 | /* Copyright 2014 Nicolas Laplante 2 | * 3 | * This file is part of envelope. 4 | * 5 | * envelope is free software: you can redistribute it 6 | * and/or modify it under the terms of the GNU General Public License as 7 | * published by the Free Software Foundation, either version 3 of the 8 | * License, or (at your option) any later version. 9 | * 10 | * envelope is distributed in the hope that it will be 11 | * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 13 | * Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with envelope. If not, see http://www.gnu.org/licenses/. 17 | */ 18 | 19 | using Gee; 20 | 21 | namespace Envelope.Service { 22 | public errordomain ImporterError { 23 | ENOENT, 24 | UNSUPPORTED, 25 | PARSE 26 | } 27 | 28 | /** 29 | * 30 | */ 31 | public interface Importer : Object { 32 | /** 33 | * 34 | */ 35 | public abstract ArrayList import (string path) throws ServiceError, ImporterError; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/service/PayeeStore.vala: -------------------------------------------------------------------------------- 1 | /* Copyright 2014 Nicolas Laplante 2 | * 3 | * This file is part of envelope. 4 | * 5 | * envelope is free software: you can redistribute it 6 | * and/or modify it under the terms of the GNU General Public License as 7 | * published by the Free Software Foundation, either version 3 of the 8 | * License, or (at your option) any later version. 9 | * 10 | * envelope is distributed in the hope that it will be 11 | * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 13 | * Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with envelope. If not, see http://www.gnu.org/licenses/. 17 | */ 18 | 19 | using Envelope.Database; 20 | using Gee; 21 | 22 | namespace Envelope.Service { 23 | public class PayeeStore : Gtk.ListStore { 24 | private static PayeeStore payee_store_instance = null; 25 | 26 | public static new unowned PayeeStore get_default () { 27 | if (payee_store_instance == null) { 28 | payee_store_instance = new PayeeStore (); 29 | } 30 | 31 | return payee_store_instance; 32 | } 33 | 34 | public const int COLUMN = 0; 35 | 36 | private PayeeStore () { 37 | Object (); 38 | set_column_types ({typeof (string), typeof (int)}); 39 | reload (); 40 | } 41 | 42 | public void reload () { 43 | clear (); 44 | try { 45 | load_payees (); 46 | } catch (DatabaseError err) { 47 | warning ("could not load payees; transaction search autocompletion won't work (%s)".printf (err.message)); 48 | } 49 | } 50 | 51 | private void load_payees () throws DatabaseError { 52 | Collection payees = DatabaseManager.get_default ().get_payees (); 53 | 54 | if (!payees.is_empty) { 55 | foreach (Payee m in payees) { 56 | Gtk.TreeIter iter; 57 | append (out iter); 58 | 59 | @set (iter, COLUMN, m.label, 1, m.occurences, -1); 60 | } 61 | } 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/service/QIFImporter.vala: -------------------------------------------------------------------------------- 1 | /* Copyright 2014 Nicolas Laplante 2 | * 3 | * This file is part of envelope. 4 | * 5 | * envelope is free software: you can redistribute it 6 | * and/or modify it under the terms of the GNU General Public License as 7 | * published by the Free Software Foundation, either version 3 of the 8 | * License, or (at your option) any later version. 9 | * 10 | * envelope is distributed in the hope that it will be 11 | * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 13 | * Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with envelope. If not, see http://www.gnu.org/licenses/. 17 | */ 18 | 19 | using Gee; 20 | 21 | namespace Envelope.Service { 22 | private static QIFImporter qif_importer_instance = null; 23 | 24 | public class QIFImporter : Object, Importer { 25 | private QIFImporter () { 26 | Object (); 27 | qif_importer_instance = this; 28 | } 29 | 30 | private const char LINE_TYPE_TRANSACTION_DELIMITER = '^'; 31 | private const char LINE_TYPE_DATE = 'D'; 32 | private const char LINE_TYPE_AMOUNT = 'T'; 33 | private const char LINE_TYPE_MEMO = 'M'; 34 | private const char LINE_TYPE_CLEARED = 'C'; 35 | private const char LINE_TYPE_CHECK_NUM = 'C'; 36 | private const char LINE_TYPE_PAYEE = 'P'; 37 | private const char LINE_TYPE_PAYEE_ADDRESS = 'A'; 38 | private const char LINE_TYPE_CATEGORY = 'L'; 39 | private const char LINE_TYPE_REIMBURSABLE = 'F'; 40 | private const char LINE_TYPE_SPLIT = 'S'; 41 | private const char LINE_TYPE_SPLIT_MEMO = 'E'; 42 | private const char LINE_TYPE_SPLIT_AMOUNT = '$'; 43 | private const char LINE_TYPE_SPLIT_PERCENT = '%'; 44 | private const char LINE_TYPE_INVESTMENT = 'N'; 45 | private const char LINE_TYPE_SECURITY_NAME = 'Y'; 46 | private const char LINE_TYPE_PRICE = 'I'; 47 | private const char LINE_TYPE_SHARE_QTY_SPLIT_RATIO = 'Q'; 48 | private const char LINE_TYPE_COMMISSION_COST = 'O'; 49 | private const char LINE_TYPE_QUICKEN_EXTENDED = 'X'; 50 | private const char LINE_TYPE_QUICKEN_EXTENDED_SHIP_TO_ADDRESS = 'A'; 51 | private const char LINE_TYPE_QUICKEN_EXTENDED_TYPE = 'I'; 52 | private const char LINE_TYPE_QUICKEN_EXTENDED_INVOICE_DUE_DATE = 'E'; 53 | private const char LINE_TYPE_QUICKEN_EXTENDED_TAX_ACCOUNT = 'C'; 54 | private const char LINE_TYPE_QUICKEN_EXTENDED_TAX_RATE = 'R'; 55 | private const char LINE_TYPE_QUICKEN_EXTENDED_TAX_AMOUNT = 'T'; 56 | private const char LINE_TYPE_QUICKEN_EXTENDED_LINE_ITEM_DESCRIPTION = 'S'; 57 | private const char LINE_TYPE_QUICKEN_EXTENDED_LINE_ITEM_CATEGORY = 'N'; 58 | private const char LINE_TYPE_QUICKEN_EXTENDED_LINE_ITEM_QTY = '#'; 59 | private const char LINE_TYPE_QUICKEN_EXTENDED_LINE_ITEM_UNIT_PRICE = '$'; 60 | private const char LINE_TYPE_QUICKEN_EXTENDED_LINE_ITEM_TAXABLE = 'F'; 61 | 62 | private struct QIFTransaction { 63 | bool split; 64 | string date; 65 | double amount; 66 | string memo; 67 | string clear_status; 68 | string check_num; 69 | string payee; 70 | string payee_address; 71 | string category; 72 | string reimursable; 73 | double split_percentage; 74 | string inv_action; 75 | string security_name; 76 | string security_price; 77 | string share_qty; 78 | string comm_cost; 79 | double xfer_amount; 80 | } 81 | 82 | public static new unowned QIFImporter get_default () { 83 | if (qif_importer_instance == null) { 84 | qif_importer_instance = new QIFImporter (); 85 | } 86 | 87 | return qif_importer_instance; 88 | } 89 | 90 | public string date_delimiter { get; set; default = "/"; } 91 | 92 | /** 93 | * Import transactions from the QIF file specified by path 94 | * 95 | * @param string path the path to the QIF file 96 | * @return ArrayList the list of imported transactions 97 | * @throws ServiceError 98 | * @throws ImporterError 99 | */ 100 | public ArrayList import (string path) throws ServiceError, ImporterError { 101 | ArrayList list = new ArrayList (); 102 | 103 | var file = File.new_for_path (path); 104 | 105 | if (!file.query_exists ()) { 106 | throw new ServiceError.ENOENT ("specified file does not exist"); 107 | } 108 | 109 | try { 110 | var input_stream = file.read (); 111 | var stream = new DataInputStream (input_stream); 112 | 113 | // discard first line 114 | stream.read_line (); // eg.: !Type:Bank 115 | 116 | // now we must parse each section which are delimited by a single '^' line 117 | QIFTransaction transaction = QIFTransaction (); 118 | string? line = null; 119 | 120 | do { 121 | line = stream.read_line (); 122 | 123 | if (line != null && line.length > 0) { 124 | if (!parse_line (line.strip (), ref transaction)) { 125 | // transaction is complete; create a real Transaction object from the 126 | // struct and add it to the list 127 | Transaction trans; 128 | 129 | if (transaction.split) { 130 | // TODO find the first transaction which is split == false 131 | // in the items before this one in the list 132 | // this is the parent transaction 133 | } 134 | 135 | qif_transaction_to_transaction (transaction, out trans); 136 | 137 | list.add (trans); 138 | 139 | // try new transaction 140 | transaction = QIFTransaction (); 141 | } 142 | } 143 | } while (line != null); 144 | } catch (Error err) { 145 | throw new ServiceError.IO (err.message); 146 | } 147 | 148 | info ("imported %d transactions from %s".printf (list.size, path)); 149 | 150 | return list; 151 | } 152 | 153 | private bool parse_line (string line, ref QIFTransaction transaction) { 154 | assert (line.length > 0); 155 | 156 | char type = line.@get (0); 157 | 158 | if (type == LINE_TYPE_TRANSACTION_DELIMITER) { 159 | return false; 160 | } 161 | 162 | // skip if line type is not supported (yet) 163 | if (!line_type_supported (type)) { 164 | return true; 165 | } 166 | 167 | var payload = line.substring (1); 168 | 169 | switch (type) { 170 | case LINE_TYPE_SPLIT: 171 | transaction.split = true; 172 | transaction.category = payload; 173 | break; 174 | case LINE_TYPE_SPLIT_MEMO: 175 | transaction.split = true; 176 | transaction.memo = payload; 177 | break; 178 | case LINE_TYPE_SPLIT_AMOUNT: 179 | transaction.split = true; 180 | transaction.amount = double.parse (payload); 181 | break; 182 | case LINE_TYPE_SPLIT_PERCENT: 183 | transaction.split = true; 184 | transaction.split_percentage = double.parse (payload); 185 | break; 186 | case LINE_TYPE_DATE: 187 | transaction.date = payload; 188 | break; 189 | case LINE_TYPE_AMOUNT: 190 | transaction.amount = double.parse (payload); 191 | break; 192 | case LINE_TYPE_MEMO: 193 | transaction.memo = payload; 194 | break; 195 | case LINE_TYPE_CLEARED: 196 | transaction.clear_status = payload; 197 | break; 198 | case LINE_TYPE_PAYEE: 199 | transaction.payee = payload; 200 | break; 201 | case LINE_TYPE_CATEGORY: 202 | transaction.category = payload; 203 | break; 204 | default: 205 | debug ("type %s ignored".printf (type.to_string ())); 206 | break; 207 | } 208 | 209 | return true; 210 | } 211 | 212 | private void qif_transaction_to_transaction (QIFTransaction transaction, out Transaction trans) { 213 | trans = new Transaction (); 214 | 215 | trans.label = transaction.payee.strip (); 216 | trans.description = transaction.memo != null ? transaction.memo.strip () : null; 217 | trans.amount = Math.fabs (transaction.amount); 218 | 219 | trans.direction = transaction.amount < 0 ? Transaction.Direction.OUTGOING : Transaction.Direction.INCOMING; 220 | 221 | // parse date 222 | DateTime dt; 223 | if (!parse_qif_date_string (transaction.date, out dt)) { 224 | error ("could not parse date string %s".printf (transaction.date)); 225 | } 226 | 227 | trans.date = dt; 228 | } 229 | 230 | // this is so wrong 231 | private bool parse_qif_date_string (string input, out DateTime date) { 232 | string[] tokens = input.split (date_delimiter); 233 | 234 | string year = tokens[2].strip (); 235 | if (year.length == 2) { 236 | year = "20" + year; 237 | } 238 | 239 | date = new DateTime.local (int.parse (year), int.parse (tokens[0]), int.parse (tokens[1]), 0, 0, 0d); 240 | 241 | return true; 242 | } 243 | 244 | private static bool line_type_supported (char type) { 245 | switch (type) { 246 | case LINE_TYPE_DATE: 247 | case LINE_TYPE_AMOUNT: 248 | case LINE_TYPE_MEMO: 249 | case LINE_TYPE_CLEARED: 250 | case LINE_TYPE_PAYEE: 251 | case LINE_TYPE_SPLIT: 252 | case LINE_TYPE_SPLIT_MEMO: 253 | case LINE_TYPE_SPLIT_AMOUNT: 254 | case LINE_TYPE_SPLIT_PERCENT: 255 | return true; 256 | 257 | default: 258 | return false; 259 | } 260 | } 261 | } 262 | } 263 | -------------------------------------------------------------------------------- /src/service/settings/SavedState.vala: -------------------------------------------------------------------------------- 1 | /* Copyright 2014 Nicolas Laplante 2 | * 3 | * This file is part of envelope. 4 | * 5 | * envelope is free software: you can redistribute it 6 | * and/or modify it under the terms of the GNU General Public License as 7 | * published by the Free Software Foundation, either version 3 of the 8 | * License, or (at your option) any later version. 9 | * 10 | * envelope is distributed in the hope that it will be 11 | * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 13 | * Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with envelope. If not, see http://www.gnu.org/licenses/. 17 | */ 18 | 19 | namespace Envelope.Service.Settings { 20 | private static SavedState saved_state_instance = null; 21 | 22 | public class SavedState : Granite.Services.Settings { 23 | public bool maximized { get; set; default = false; } 24 | 25 | public int window_width { get; set; } 26 | public int window_height { get; set; } 27 | 28 | public int sidebar_width { get; set; } 29 | 30 | public string search_term { get; set; } 31 | 32 | public int selected_account_id { get; set; } 33 | public int selected_category_id { get; set; } 34 | 35 | public Envelope.View.FilterView.FilterType filter_type { get; set; } 36 | 37 | public DateTime from_date { get; set; } 38 | public DateTime to_date { get; set; } 39 | 40 | public static SavedState get_default () { 41 | if (saved_state_instance == null) { 42 | saved_state_instance = new SavedState (); 43 | } 44 | 45 | return saved_state_instance; 46 | } 47 | 48 | private SavedState () { 49 | base ("com.github.cjfloss.envelope.settings"); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/util/Date.vala: -------------------------------------------------------------------------------- 1 | /* Copyright 2014 Nicolas Laplante 2 | * 3 | * This file is part of envelope. 4 | * 5 | * envelope is free software: you can redistribute it 6 | * and/or modify it under the terms of the GNU General Public License as 7 | * published by the Free Software Foundation, either version 3 of the 8 | * License, or (at your option) any later version. 9 | * 10 | * envelope is distributed in the hope that it will be 11 | * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 13 | * Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with envelope. If not, see http://www.gnu.org/licenses/. 17 | */ 18 | 19 | namespace Envelope.Util.Date { 20 | /** 21 | * Get start and end dates for the specified month and year 22 | * 23 | * @param month the month number (starting at 1) 24 | * @param year the year 25 | * @param start the output start date of the month 26 | * @param end the output ending date of the month 27 | */ 28 | public void get_month_boundaries (int month, int year, out DateTime start, out DateTime end) { 29 | start = new DateTime.local (year, month, 1, 0, 0, 0); 30 | end = new DateTime.local (year, month, GLib.Date.get_days_in_month ((DateMonth) month, (DateYear) year), 23, 59, 59); 31 | } 32 | 33 | /** 34 | * Get the current year and month 35 | * 36 | * @param month the output month number 37 | * @param year the output year number 38 | */ 39 | public void get_year_month (out int month, out int year) { 40 | var now = new DateTime.now_local (); 41 | 42 | month = now.get_month (); 43 | year = now.get_year (); 44 | } 45 | 46 | /** 47 | * Get the month and year numbers for the specified number of months before now 48 | * 49 | * @param ago the number of months before now 50 | * @param month the output month number 51 | * @param year the output year number 52 | */ 53 | public void months_ago (int ago, out int month, out int year) { 54 | var months_ago = new DateTime.now_local ().add_months (-ago); 55 | 56 | month = months_ago.get_month (); 57 | year = months_ago.get_year (); 58 | 59 | debug ("months_ago (%d): %d-%d", ago, year, month); 60 | } 61 | 62 | /** 63 | * Get the date for tomorrow 64 | * 65 | * @param tomorrow the output date for tomorrow 66 | */ 67 | public void tomorrow (out DateTime tomorrow) { 68 | var dt = new DateTime.now_local ().add_days (1); 69 | tomorrow = new DateTime.local (dt.get_year (), dt.get_month (), dt.get_day_of_month (), 0, 0, 0); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/util/String.vala: -------------------------------------------------------------------------------- 1 | /* Copyright 2014 Nicolas Laplante 2 | * 3 | * This file is part of envelope. 4 | * 5 | * envelope is free software: you can redistribute it 6 | * and/or modify it under the terms of the GNU General Public License as 7 | * published by the Free Software Foundation, either version 3 of the 8 | * License, or (at your option) any later version. 9 | * 10 | * envelope is distributed in the hope that it will be 11 | * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 13 | * Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with envelope. If not, see http://www.gnu.org/licenses/. 17 | */ 18 | 19 | namespace Envelope.Util.String { 20 | public const string ELLIPSIS = "\u2026"; 21 | private static char* currency_symbol = null; 22 | 23 | public errordomain ParseError { 24 | INVALID 25 | } 26 | 27 | public static string ellipsize (string input) { 28 | return "%s%s".printf (input, ELLIPSIS); 29 | } 30 | 31 | public static string format_currency (double amount, bool currency_symbol = true) { 32 | string format = currency_symbol ? "%11n" : "%!11n"; 33 | 34 | char[] buffer = new char[double.DTOSTR_BUF_SIZE]; 35 | Monetary.strfmon (buffer, format, amount); 36 | 37 | return ((string) buffer).strip (); 38 | } 39 | 40 | public char* get_currency_symbol () { 41 | if (currency_symbol != null) { 42 | return currency_symbol; 43 | } 44 | 45 | Monetary.lconv *locale_info = Monetary.localeconv (); 46 | currency_symbol = locale_info->currency_symbol; 47 | 48 | return currency_symbol; 49 | } 50 | 51 | public static double parse_currency (string amount) throws ParseError { 52 | if (regex_parse_currency == null) { 53 | initialize_currency_regex (); 54 | } 55 | 56 | double result; 57 | 58 | // replace all non-currency characters (except . and separator) 59 | string sanitized = amount; 60 | try { 61 | sanitized = regex_parse_currency.replace_literal (amount, -1, 0, ""); 62 | } catch (RegexError err) { 63 | error ("error occured while sanitizing input string '%s' (%s))", amount, err.message); 64 | } 65 | 66 | char *r; 67 | result = Monetary.strtod (sanitized, &r); 68 | 69 | return result; 70 | } 71 | 72 | // regular expression to strip everything but numbers and currency-specific characters 73 | private static Regex regex_parse_currency; 74 | 75 | // initializer for the currency regexp 76 | private static void initialize_currency_regex () { 77 | Monetary.lconv *locale_info = Monetary.localeconv (); 78 | 79 | try { 80 | debug ("decimal point: %c", * (locale_info->decimal_point)); 81 | 82 | regex_parse_currency = new Regex ("[^0-9\\%c\\-]*".printf (*(locale_info->decimal_point))); 83 | } catch (RegexError err) { 84 | error (err.message); 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/vapi/monetary.vapi: -------------------------------------------------------------------------------- 1 | /* Copyright 2014 Nicolas Laplante 2 | * 3 | * This file is part of envelope. 4 | * 5 | * envelope is free software: you can redistribute it 6 | * and/or modify it under the terms of the GNU General Public License as 7 | * published by the Free Software Foundation, either version 3 of the 8 | * License, or (at your option) any later version. 9 | * 10 | * envelope is distributed in the hope that it will be 11 | * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 13 | * Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with envelope. If not, see http://www.gnu.org/licenses/. 17 | */ 18 | 19 | namespace Monetary { 20 | 21 | [CCode (cheader_filename = "locale.h", cname = "struct lconv", has_type_id = false)] 22 | [SimpleType] 23 | public struct lconv { 24 | char* decimal_point; 25 | char* mon_decimal_point; 26 | char* thousands_sep; 27 | char* mon_thousands_sep; 28 | char* grouping; 29 | char* mon_grouping; 30 | char* int_frac_digits; 31 | char* frac_digits; 32 | char* currency_symbol; 33 | char* int_currency_symbol; 34 | char p_cs_precedes; 35 | char n_cs_precedes; 36 | char p_sep_by_space; 37 | char n_sep_by_space; 38 | char* positive_sign; 39 | char* negative_sign; 40 | char p_sign_posn; 41 | char n_sign_posn; 42 | } 43 | 44 | [CCode (cheader_filename = "locale.h", cname = "localeconv")] 45 | public lconv* localeconv (); 46 | 47 | [CCode(cheader_filename = "monetary.h", cname = "strfmon")] 48 | static ssize_t strfmon(char[] s, string format, double data); 49 | 50 | [CCode (cname = "strtod")] 51 | static double strtod (string input, char** last); 52 | } 53 | -------------------------------------------------------------------------------- /src/view/AccountWelcomeScreen.vala: -------------------------------------------------------------------------------- 1 | /* Copyright 2014 Nicolas Laplante 2 | * 3 | * This file is part of envelope. 4 | * 5 | * envelope is free software: you can redistribute it 6 | * and/or modify it under the terms of the GNU General Public License as 7 | * published by the Free Software Foundation, either version 3 of the 8 | * License, or (at your option) any later version. 9 | * 10 | * envelope is distributed in the hope that it will be 11 | * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 13 | * Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with envelope. If not, see http://www.gnu.org/licenses/. 17 | */ 18 | 19 | namespace Envelope.View { 20 | public class AccountWelcomeScreen : Granite.Widgets.Welcome { 21 | private static AccountWelcomeScreen account_welcome_screen_instance = null; 22 | 23 | public static new AccountWelcomeScreen get_default () { 24 | if (account_welcome_screen_instance == null) { 25 | account_welcome_screen_instance = new AccountWelcomeScreen (); 26 | } 27 | 28 | return account_welcome_screen_instance; 29 | } 30 | 31 | private enum Action { 32 | ADD_TRANSACTION, 33 | IMPORT_TRANSACTIONS 34 | } 35 | 36 | public Account account { get; set; } 37 | 38 | public signal void add_transaction_selected (Account account); 39 | 40 | public AccountWelcomeScreen () { 41 | base (_("Spend! Get paid!"), _("There are currently no transactions in this account")); 42 | build_ui (); 43 | connect_signals (); 44 | } 45 | 46 | private void build_ui () { 47 | append ("add", _("Record a transaction"), _("Record a fresh new transaction for this account. Or, plan a future one!")); 48 | append ("document-import", _("Import transactions"), _("Import from a QIF file obtained from another application")); 49 | show_all (); 50 | } 51 | 52 | private void connect_signals () { 53 | activated.connect (item_activated); 54 | } 55 | 56 | private void item_activated (int index ) { 57 | switch (index) { 58 | 59 | case Action.ADD_TRANSACTION: 60 | add_transaction_selected (account); 61 | break; 62 | case Action.IMPORT_TRANSACTIONS: 63 | var view = TransactionView.get_default (); 64 | view.transactions = account.transactions; 65 | view.show_import_dialog (); 66 | break; 67 | default: 68 | assert_not_reached (); 69 | } 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/view/CategoryProperties.vala: -------------------------------------------------------------------------------- 1 | /* Copyright 2014 Nicolas Laplante 2 | * 3 | * This file is part of envelope. 4 | * 5 | * envelope is free software: you can redistribute it 6 | * and/or modify it under the terms of the GNU General Public License as 7 | * published by the Free Software Foundation, either version 3 of the 8 | * License, or (at your option) any later version. 9 | * 10 | * envelope is distributed in the hope that it will be 11 | * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 13 | * Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with envelope. If not, see http://www.gnu.org/licenses/. 17 | */ 18 | 19 | using Envelope.Widget; 20 | using Envelope.Service; 21 | 22 | namespace Envelope.View { 23 | public class CategoryProperties : CellRendererUpdatable { 24 | private const int ENTRY_WIDTH = 15; 25 | 26 | public MonthlyCategory category { get; set; } 27 | public double inflow { get; set; } 28 | public double outflow { get; set; } 29 | 30 | private Gtk.Entry category_name_entry; 31 | private Gtk.Entry budgeted_amount_entry; 32 | private Gtk.Label inflow_label; 33 | private Gtk.Label inflow_label_label; 34 | private Gtk.Label outflow_label; 35 | private Gtk.Label outflow_label_label; 36 | private Gtk.Label remaining_label; 37 | private Gtk.Label remaining_label_label; 38 | private Gtk.Label available_label; 39 | private Gtk.Button ok_button; 40 | private Gtk.Button cancel_button; 41 | 42 | private BudgetManager bm = BudgetManager.get_default (); 43 | 44 | public CategoryProperties () { 45 | Object (); 46 | build_ui (); 47 | connect_signals (); 48 | } 49 | 50 | public override void update () { 51 | category_name_entry.text = category.name; 52 | budgeted_amount_entry.text = category.amount_budgeted != 0d ? Envelope.Util.String.format_currency (category.amount_budgeted) : ""; 53 | 54 | inflow_label.label = Envelope.Util.String.format_currency (inflow); 55 | inflow_label.visible = inflow > 0d; 56 | inflow_label_label.visible = inflow_label.visible; 57 | 58 | outflow_label.label = Envelope.Util.String.format_currency (outflow); 59 | outflow_label.visible = outflow > 0d; 60 | outflow_label_label.visible = outflow_label.visible; 61 | 62 | double remaining = category.amount_budgeted - outflow + inflow; 63 | remaining_label.label = Envelope.Util.String.format_currency (remaining); 64 | remaining_label.visible = category.amount_budgeted != 0d; 65 | remaining_label_label.visible = remaining_label.visible; 66 | 67 | available_label.label = "Max. %s".printf (Envelope.Util.String.format_currency (BudgetManager.get_default ().state.budget_available)); 68 | } 69 | 70 | private void build_ui () { 71 | column_spacing = 10; 72 | row_spacing = 10; 73 | //column_homogeneous = true; 74 | 75 | // title 76 | var title_label = new Gtk.Label (_("Edit category")); 77 | title_label.get_style_context ().add_class (Granite.STYLE_CLASS_H3_LABEL); 78 | attach_next_to (title_label, null, Gtk.PositionType.TOP, 3, 1); 79 | 80 | // category name text entry 81 | var category_name_label = new Gtk.Label (_("Name")); 82 | category_name_label.xalign = 1.0f; 83 | attach_next_to (category_name_label, title_label, Gtk.PositionType.BOTTOM, 1, 1); 84 | 85 | category_name_entry = new Gtk.Entry (); 86 | category_name_entry.width_chars = ENTRY_WIDTH; 87 | category_name_entry.max_width_chars = ENTRY_WIDTH; 88 | attach_next_to (category_name_entry, category_name_label, Gtk.PositionType.RIGHT, 2, 1); 89 | 90 | // budgeted amount 91 | var budgeted_amount_label = new Gtk.Label (_("Budget")); 92 | budgeted_amount_label.xalign = 1.0f; 93 | attach_next_to (budgeted_amount_label, null, Gtk.PositionType.BOTTOM, 1, 1); 94 | 95 | budgeted_amount_entry = new Gtk.Entry (); 96 | budgeted_amount_entry.width_chars = ENTRY_WIDTH; 97 | budgeted_amount_entry.max_width_chars = ENTRY_WIDTH; 98 | budgeted_amount_entry.placeholder_text = _("Monthly budget"); 99 | attach_next_to (budgeted_amount_entry, budgeted_amount_label, Gtk.PositionType.RIGHT, 1, 1); 100 | 101 | available_label = new Gtk.Label (""); 102 | available_label.xalign = 0.0f; 103 | attach_next_to (available_label, budgeted_amount_entry, Gtk.PositionType.RIGHT, 1, 1); 104 | 105 | // outflow 106 | outflow_label_label = new Gtk.Label (_("Outflow")); 107 | outflow_label_label.xalign = 1.0f; 108 | attach_next_to (outflow_label_label, null, Gtk.PositionType.BOTTOM, 1, 1); 109 | 110 | outflow_label = new Gtk.Label (""); 111 | outflow_label.xalign = 0.0f; 112 | attach_next_to (outflow_label, outflow_label_label, Gtk.PositionType.RIGHT, 1, 1); 113 | 114 | // inflow 115 | inflow_label_label = new Gtk.Label (_("Inflow")); 116 | inflow_label_label.xalign = 1.0f; 117 | attach_next_to (inflow_label_label, null, Gtk.PositionType.BOTTOM, 1, 1); 118 | 119 | inflow_label = new Gtk.Label (""); 120 | inflow_label.xalign = 0.0f; 121 | attach_next_to (inflow_label, inflow_label_label, Gtk.PositionType.RIGHT, 1, 1); 122 | 123 | // remaining 124 | remaining_label_label = new Gtk.Label (_("Remaining")); 125 | remaining_label_label.xalign = 1.0f; 126 | attach_next_to (remaining_label_label, null, Gtk.PositionType.BOTTOM, 1, 1); 127 | 128 | remaining_label = new Gtk.Label (""); 129 | remaining_label.xalign = 0.0f; 130 | attach_next_to (remaining_label, remaining_label_label, Gtk.PositionType.RIGHT, 1, 1); 131 | 132 | // button box 133 | var box = new Gtk.ButtonBox (Gtk.Orientation.HORIZONTAL); 134 | box.set_layout (Gtk.ButtonBoxStyle.END); 135 | box.set_spacing (5); 136 | attach_next_to (box, null, Gtk.PositionType.BOTTOM, 3, 1); 137 | 138 | // Cancel button 139 | cancel_button = new Gtk.Button.with_label (_("Cancel")); 140 | box.add (cancel_button); 141 | 142 | // OK button 143 | ok_button = new Gtk.Button.with_label (_("Apply")); 144 | ok_button.get_style_context ().add_class ("suggested-action"); 145 | box.add (ok_button); 146 | 147 | show_all (); 148 | } 149 | 150 | private void connect_signals () { 151 | cancel_button.clicked.connect (cancel_clicked); 152 | ok_button.clicked.connect (ok_clicked); 153 | } 154 | 155 | private void cancel_clicked () { 156 | dismiss (); 157 | } 158 | 159 | private void ok_clicked () { 160 | try { 161 | category.name = category_name_entry.text.strip (); 162 | category.amount_budgeted = Envelope.Util.String.parse_currency (budgeted_amount_entry.text); 163 | } catch (Envelope.Util.String.ParseError err) { 164 | assert_not_reached (); 165 | } 166 | 167 | try { 168 | bm.set_current_budgeted_amount (category); 169 | bm.update_category (category); 170 | } catch (ServiceError err) { 171 | error ("could not update category (%s)", err.message); 172 | } 173 | 174 | dismiss (); 175 | } 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /src/view/FilterView.vala: -------------------------------------------------------------------------------- 1 | /* Copyright 2014 Nicolas Laplante 2 | * 3 | * This file is part of envelope. 4 | * 5 | * envelope is free software: you can redistribute it 6 | * and/or modify it under the terms of the GNU General Public License as 7 | * published by the Free Software Foundation, either version 3 of the 8 | * License, or (at your option) any later version. 9 | * 10 | * envelope is distributed in the hope that it will be 11 | * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 13 | * Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with envelope. If not, see http://www.gnu.org/licenses/. 17 | */ 18 | 19 | namespace Envelope.View { 20 | public class FilterView : Gtk.Box { 21 | private static FilterView filter_view_instance = null; 22 | 23 | public static new FilterView get_default () { 24 | if (filter_view_instance == null) { 25 | filter_view_instance = new FilterView (); 26 | } 27 | 28 | return filter_view_instance; 29 | } 30 | 31 | public signal void date_filter_changed (); 32 | 33 | public DateTime from { get; private set; } 34 | public DateTime to { get; private set; } 35 | 36 | public enum FilterType { 37 | THIS_MONTH, 38 | LAST_MONTH, 39 | FUTURE, 40 | MANUAL 41 | } 42 | 43 | public FilterType filter_type { get; set; } 44 | 45 | public Gtk.RadioButton btn_this_month { get; private set; } 46 | public Gtk.RadioButton btn_last_month { get; private set; } 47 | public Gtk.RadioButton btn_future { get; private set; } 48 | public Gtk.RadioButton btn_manual { get; private set; } 49 | public Granite.Widgets.DatePicker from_date { get; private set; } 50 | public Granite.Widgets.DatePicker to_date { get; private set; } 51 | 52 | private FilterView () { 53 | Object (orientation: Gtk.Orientation.VERTICAL); 54 | 55 | filter_type = FilterType.THIS_MONTH; 56 | 57 | DateTime d_from, d_to; 58 | 59 | compute_dates (out d_from, out d_to); 60 | from = d_from; 61 | to = d_to; 62 | 63 | build_ui (); 64 | connect_signals (); 65 | 66 | filter_view_instance = this; 67 | } 68 | 69 | private void build_ui () { 70 | debug ("build filter ui"); 71 | 72 | set_spacing (5); 73 | 74 | var inner_box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 12); 75 | pack_start (inner_box); 76 | 77 | var title_image = new Gtk.Image.from_icon_name ("office-calendar", Gtk.IconSize.LARGE_TOOLBAR); 78 | inner_box.pack_start (title_image, false, false); 79 | 80 | // this month 81 | btn_this_month = new Gtk.RadioButton (null); 82 | btn_this_month.label = _("This month"); 83 | inner_box.pack_start (btn_this_month, false, false); 84 | 85 | // last month 86 | btn_last_month = new Gtk.RadioButton.with_label_from_widget (btn_this_month, _("Last month")); 87 | inner_box.pack_start (btn_last_month, false, false); 88 | 89 | // future 90 | btn_future = new Gtk.RadioButton.with_label_from_widget (btn_this_month, _("Future")); 91 | inner_box.pack_start (btn_future, false, false); 92 | 93 | // manual dates 94 | btn_manual = new Gtk.RadioButton.with_label_from_widget (btn_this_month, _("Pick dates:")); 95 | inner_box.pack_start (btn_manual, false, false); 96 | 97 | from_date = new Granite.Widgets.DatePicker (); 98 | from_date.sensitive = false; 99 | from_date.date = new DateTime.now_local ().add_months (-1); 100 | inner_box.pack_start (from_date, false, false); 101 | 102 | to_date = new Granite.Widgets.DatePicker (); 103 | to_date.sensitive = false; 104 | to_date.date = new DateTime.now_local (); 105 | inner_box.pack_start (to_date, false, false); 106 | } 107 | 108 | private void connect_signals () { 109 | debug ("connect filter view signals"); 110 | 111 | notify["filter-type"].connect ((s, p) => { 112 | switch (filter_type) { 113 | case FilterType.THIS_MONTH: 114 | btn_this_month.active = true; 115 | break; 116 | case FilterType.LAST_MONTH: 117 | btn_last_month.active = true; 118 | break; 119 | case FilterType.FUTURE: 120 | btn_future.active = true; 121 | break; 122 | case FilterType.MANUAL: 123 | btn_manual.active = true; 124 | break; 125 | default: 126 | assert_not_reached (); 127 | } 128 | }); 129 | 130 | btn_last_month.toggled.connect (() => { 131 | if (btn_last_month.get_active ()) { 132 | filter_type = FilterType.LAST_MONTH; 133 | fire_date_filter_changed_signal (); 134 | } 135 | }); 136 | 137 | btn_this_month.toggled.connect (() => { 138 | if (btn_this_month.get_active ()) { 139 | filter_type = FilterType.THIS_MONTH; 140 | fire_date_filter_changed_signal (); 141 | } 142 | }); 143 | 144 | btn_future.toggled.connect (() => { 145 | if (btn_future.get_active ()) { 146 | filter_type = FilterType.FUTURE; 147 | fire_date_filter_changed_signal (); 148 | } 149 | }); 150 | 151 | btn_manual.toggled.connect (() => { 152 | if (btn_manual.get_active ()) { 153 | filter_type = FilterType.MANUAL; 154 | 155 | from_date.sensitive = true; 156 | to_date.sensitive = true; 157 | 158 | if (from_date.date != null && to_date.date != null) { 159 | fire_date_filter_changed_signal (); 160 | } 161 | } else { 162 | from_date.sensitive = false; 163 | to_date.sensitive = false; 164 | } 165 | }); 166 | 167 | from_date.notify["date"].connect (() => { 168 | if (btn_manual.get_active () && from_date.date != null && to_date.date != null) { 169 | fire_date_filter_changed_signal (); 170 | } 171 | }); 172 | 173 | to_date.notify["date"].connect (() => { 174 | if (btn_manual.get_active () && from_date.date != null && to_date.date != null) { 175 | fire_date_filter_changed_signal (); 176 | } 177 | }); 178 | } 179 | 180 | private void compute_dates (out DateTime? from, out DateTime? to) { 181 | int month, year; 182 | Envelope.Util.Date.get_year_month (out year, out month); 183 | 184 | switch (filter_type) { 185 | case FilterType.THIS_MONTH: 186 | Envelope.Util.Date.get_month_boundaries (year, month, out from, out to); 187 | break; 188 | case FilterType.LAST_MONTH: 189 | int last_month, last_year; 190 | Envelope.Util.Date.months_ago (1, out last_year, out last_month); 191 | 192 | Envelope.Util.Date.get_month_boundaries (last_year, last_month, out from, out to); 193 | break; 194 | case FilterType.FUTURE: 195 | Envelope.Util.Date.tomorrow (out from); 196 | to = null; 197 | break; 198 | case FilterType.MANUAL: 199 | from = from_date.date; 200 | to = to_date.date; 201 | break; 202 | default: 203 | assert_not_reached (); 204 | } 205 | } 206 | 207 | private void fire_date_filter_changed_signal () { 208 | DateTime d_from, d_to; 209 | 210 | compute_dates (out d_from, out d_to); 211 | 212 | from = d_from; 213 | to = d_to; 214 | 215 | date_filter_changed (); 216 | } 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /src/view/WelcomeScreen.vala: -------------------------------------------------------------------------------- 1 | /* Copyright 2014 Nicolas Laplante 2 | * 3 | * This file is part of envelope. 4 | * 5 | * envelope is free software: you can redistribute it 6 | * and/or modify it under the terms of the GNU General Public License as 7 | * published by the Free Software Foundation, either version 3 of the 8 | * License, or (at your option) any later version. 9 | * 10 | * envelope is distributed in the hope that it will be 11 | * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 13 | * Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with envelope. If not, see http://www.gnu.org/licenses/. 17 | */ 18 | 19 | using Envelope.Dialog; 20 | 21 | namespace Envelope.View { 22 | private static Welcome welcome_instance = null; 23 | 24 | public class Welcome : Gtk.Grid { 25 | construct { 26 | var welcome = new Granite.Widgets.Welcome (_("Get your budget going"), _("You have not configured any account yet")); 27 | welcome.append ("list-add-symbolic", _("Add an account"), _("You have not configured any account yet")); 28 | 29 | add (welcome); 30 | 31 | welcome.activated.connect ((index) => { 32 | switch (index) { 33 | case 0: 34 | var dialog = new AddAccountDialog ((Gtk.Window) this.get_toplevel ()); 35 | 36 | dialog.account_created.connect ((account) => { 37 | dialog.destroy (); 38 | }); 39 | 40 | dialog.show_all (); 41 | 42 | break; 43 | default: 44 | assert_not_reached (); 45 | } 46 | }); 47 | } 48 | 49 | public static new unowned Welcome get_default () { 50 | if (welcome_instance == null) { 51 | welcome_instance = new Welcome (); 52 | } 53 | 54 | return welcome_instance; 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/widget/AbstractPopoverCellRenderer.vala: -------------------------------------------------------------------------------- 1 | /* Copyright 2014 Nicolas Laplante 2 | * 3 | * This file is part of envelope. 4 | * 5 | * envelope is free software: you can redistribute it 6 | * and/or modify it under the terms of the GNU General Public License as 7 | * published by the Free Software Foundation, either version 3 of the 8 | * License, or (at your option) any later version. 9 | * 10 | * envelope is distributed in the hope that it will be 11 | * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 13 | * Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with envelope. If not, see http://www.gnu.org/licenses/. 17 | */ 18 | 19 | namespace Envelope.Widget { 20 | /** 21 | * This is the base class for all cell renderers showing content in a 22 | * Gtk.Popover. 23 | */ 24 | public abstract class AbstractPopoverCellRenderer : CellRendererTextCompletion { 25 | public Gtk.Widget relative_to { get; set; } 26 | public Gtk.Popover popover { get; private set; } 27 | 28 | public signal void dismissed (); 29 | 30 | protected string? current_path; 31 | 32 | protected abstract void build_ui (); 33 | protected abstract void connect_signals (); 34 | 35 | protected AbstractPopoverCellRenderer (Gtk.Widget relative_to) { 36 | base (); 37 | this.relative_to = relative_to; 38 | 39 | build_real_ui (); 40 | connect_real_signals (); 41 | } 42 | 43 | public override unowned Gtk.CellEditable? start_editing ( Gdk.Event? event, Gtk.Widget widget, string path, Gdk.Rectangle background_area, Gdk.Rectangle cell_area, Gtk.CellRendererState flags) { 44 | unowned Gtk.CellEditable? return_value = base.start_editing (event, widget, path, background_area, cell_area, flags); 45 | current_path = path; 46 | 47 | popover.pointing_to = cell_area; 48 | popover.relative_to = widget; 49 | popover.set_position (Gtk.PositionType.BOTTOM); 50 | 51 | return return_value; 52 | } 53 | 54 | private void build_real_ui () { 55 | mode = Gtk.CellRendererMode.EDITABLE; 56 | editable = true; 57 | editable_set = true; 58 | 59 | popover = new Gtk.Popover (relative_to); 60 | popover.modal = true; 61 | popover.border_width = 12; 62 | popover.set_position (Gtk.PositionType.BOTTOM); 63 | 64 | build_ui (); 65 | } 66 | 67 | private void connect_real_signals () { 68 | // close popover when treeview's model changes 69 | Gtk.TreeView treeview = relative_to as Gtk.TreeView; 70 | 71 | treeview.model.row_changed.connect (() => { 72 | real_dismiss (); 73 | }); 74 | 75 | treeview.model.row_deleted.connect (() => { 76 | real_dismiss (); 77 | }); 78 | 79 | treeview.model.row_has_child_toggled.connect (() => { 80 | real_dismiss (); 81 | }); 82 | 83 | treeview.model.row_inserted.connect (() => { 84 | real_dismiss (); 85 | }); 86 | 87 | treeview.model.rows_reordered.connect (() => { 88 | real_dismiss (); 89 | }); 90 | 91 | popover.closed.connect (() => { 92 | dismissed (); 93 | }); 94 | 95 | connect_signals (); 96 | } 97 | 98 | private void real_dismiss () { 99 | popover.hide (); 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/widget/CellRendererCategoryPicker.vala: -------------------------------------------------------------------------------- 1 | /* Copyright 2014 Nicolas Laplante 2 | * 3 | * This file is part of envelope. 4 | * 5 | * envelope is free software: you can redistribute it 6 | * and/or modify it under the terms of the GNU General Public License as 7 | * published by the Free Software Foundation, either version 3 of the 8 | * License, or (at your option) any later version. 9 | * 10 | * envelope is distributed in the hope that it will be 11 | * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 13 | * Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with envelope. If not, see http://www.gnu.org/licenses/. 17 | */ 18 | 19 | namespace Envelope.Widget { 20 | public class CellRendererCategoryPicker : AbstractPopoverCellRenderer { 21 | public string payee_name { get; set; } 22 | public string category_name { get; set; } 23 | public bool apply_to_all { get; private set; } 24 | 25 | private Gtk.Entry category_entry; 26 | private Gtk.CheckButton check_button; 27 | private Gtk.Button ok_button; 28 | private Gtk.Button cancel_button; 29 | 30 | private Gtk.EntryCompletion completion; 31 | 32 | public CellRendererCategoryPicker (Gtk.Widget relative_to) { 33 | base (relative_to); 34 | } 35 | 36 | public override unowned Gtk.CellEditable? start_editing (Gdk.Event? event, Gtk.Widget widget, string path, Gdk.Rectangle background_area, Gdk.Rectangle cell_area, Gtk.CellRendererState flags) { 37 | base.start_editing (event, widget, path, background_area, cell_area, flags); 38 | 39 | check_button.label = _("Apply to all %s").printf (payee_name); 40 | 41 | completion = new Gtk.EntryCompletion (); 42 | completion.set_model (store); 43 | completion.set_text_column (text_column); 44 | completion.inline_completion = true; 45 | completion.inline_selection = true; 46 | completion.minimum_key_length = 0; 47 | completion.popup_completion = true; 48 | completion.popup_single_match = true; 49 | 50 | category_entry.completion = completion; 51 | category_entry.text = category_name; 52 | 53 | category_entry.focus.connect (() => { 54 | completion.complete (); 55 | return true; 56 | }); 57 | 58 | popover.show (); 59 | category_entry.grab_focus (); 60 | 61 | return null; 62 | } 63 | 64 | protected override void build_ui () { 65 | var grid = new Gtk.Grid (); 66 | 67 | grid.column_spacing = 10; 68 | grid.row_spacing = 10; 69 | popover.add (grid); 70 | 71 | category_entry = new Gtk.Entry (); 72 | category_entry.placeholder_text = _("Category name"); 73 | 74 | grid.attach_next_to (category_entry, null, Gtk.PositionType.BOTTOM, 2, 1); 75 | 76 | // apply to all 77 | check_button = new Gtk.CheckButton.with_label (_("Apply to all %s").printf (payee_name)); 78 | grid.attach_next_to (check_button, null, Gtk.PositionType.BOTTOM, 2, 1); 79 | 80 | // Cancel button 81 | cancel_button = new Gtk.Button.with_label (_("Cancel")); 82 | grid.attach_next_to (cancel_button, null, Gtk.PositionType.BOTTOM, 1, 1); 83 | 84 | // OK button 85 | ok_button = new Gtk.Button.with_label (_("Set category")); 86 | ok_button.get_style_context ().add_class ("suggested-action"); 87 | grid.attach_next_to (ok_button, cancel_button, Gtk.PositionType.RIGHT, 1, 1); 88 | 89 | grid.show_all (); 90 | } 91 | 92 | protected override void connect_signals () { 93 | cancel_button.clicked.connect (() => { 94 | check_button.active = false; 95 | popover.hide (); 96 | }); 97 | 98 | ok_button.clicked.connect (() => { 99 | edited (current_path, category_entry.text); 100 | check_button.active = false; 101 | popover.hide (); 102 | }); 103 | 104 | check_button.toggled.connect (() => { 105 | apply_to_all = check_button.active; 106 | }); 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/widget/CellRendererDatePicker.vala: -------------------------------------------------------------------------------- 1 | /* Copyright 2014 Nicolas Laplante 2 | * 3 | * This file is part of envelope. 4 | * 5 | * envelope is free software: you can redistribute it 6 | * and/or modify it under the terms of the GNU General Public License as 7 | * published by the Free Software Foundation, either version 3 of the 8 | * License, or (at your option) any later version. 9 | * 10 | * envelope is distributed in the hope that it will be 11 | * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 13 | * Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with envelope. If not, see http://www.gnu.org/licenses/. 17 | */ 18 | 19 | namespace Envelope.Widget { 20 | public class CellRendererDatePicker : AbstractPopoverCellRenderer { 21 | private static string date_format = "%x"; 22 | 23 | public Gtk.Calendar calendar { get; private set; } 24 | public bool date_selected { get; private set; } 25 | 26 | public CellRendererDatePicker (Gtk.Widget relative_to) { 27 | base (relative_to); 28 | } 29 | 30 | public override unowned Gtk.CellEditable ? start_editing (Gdk.Event ? event, 31 | Gtk.Widget widget, 32 | string path, 33 | Gdk.Rectangle background_area, 34 | Gdk.Rectangle cell_area, 35 | Gtk.CellRendererState flags) { 36 | base.start_editing (event, widget, path, background_area, cell_area, flags); 37 | popover.show (); 38 | 39 | return null; 40 | } 41 | 42 | protected override void build_ui () { 43 | calendar = new Gtk.Calendar (); 44 | popover.add (calendar); 45 | calendar.show_all (); 46 | } 47 | 48 | protected override void connect_signals () { 49 | calendar.day_selected.connect (select_date); 50 | } 51 | 52 | private void select_date () { 53 | popover.hide (); 54 | date_selected = true; 55 | 56 | var dt = new DateTime.local (calendar.year, calendar.month + 1,calendar.day, 0, 0, 0); 57 | 58 | edited (current_path, dt.format (date_format)); 59 | 60 | date_selected = false; 61 | current_path = null; 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/widget/CellRendererPopoverContainer.vala: -------------------------------------------------------------------------------- 1 | /* Copyright 2014 Nicolas Laplante 2 | * 3 | * This file is part of envelope. 4 | * 5 | * envelope is free software: you can redistribute it 6 | * and/or modify it under the terms of the GNU General Public License as 7 | * published by the Free Software Foundation, either version 3 of the 8 | * License, or (at your option) any later version. 9 | * 10 | * envelope is distributed in the hope that it will be 11 | * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 13 | * Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with envelope. If not, see http://www.gnu.org/licenses/. 17 | */ 18 | 19 | using Envelope.Service; 20 | 21 | namespace Envelope.Widget { 22 | public class CellRendererPopoverContainer : AbstractPopoverCellRenderer { 23 | public CellRendererUpdatable? content { get; set; } 24 | 25 | public CellRendererPopoverContainer (Gtk.Widget relative_to) { 26 | base (relative_to); 27 | } 28 | 29 | public override unowned Gtk.CellEditable ? start_editing (Gdk.Event ? event, 30 | Gtk.Widget widget, 31 | string path, 32 | Gdk.Rectangle background_area, 33 | Gdk.Rectangle cell_area, 34 | Gtk.CellRendererState flags) { 35 | unowned Gtk.CellEditable ? return_value = base.start_editing (event, widget, 36 | path, background_area, cell_area, flags); 37 | 38 | if (content == null) { 39 | return return_value; 40 | } 41 | 42 | // remove widget if needed 43 | var childs = popover.get_children (); 44 | 45 | if (childs.length () != 0 && childs.first ().data != content) { 46 | CellRendererUpdatable old_content = childs.first ().data as CellRendererUpdatable; 47 | old_content.dismiss.disconnect (on_content_dismiss); 48 | popover.remove (old_content); 49 | } 50 | 51 | content.update (); 52 | 53 | // add widget 54 | if (popover.get_children ().length () == 0) { 55 | content.dismiss.connect (on_content_dismiss); 56 | popover.add (content); 57 | } 58 | 59 | popover.show (); 60 | 61 | return null; 62 | } 63 | 64 | private void on_content_dismiss () { 65 | popover.hide (); 66 | } 67 | 68 | protected override void build_ui () { 69 | // noop 70 | } 71 | 72 | protected override void connect_signals () { 73 | // noop 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/widget/CellRendererTextCompletion.vala: -------------------------------------------------------------------------------- 1 | /* Copyright 2014 Nicolas Laplante 2 | * 3 | * This file is part of envelope. 4 | * 5 | * envelope is free software: you can redistribute it 6 | * and/or modify it under the terms of the GNU General Public License as 7 | * published by the Free Software Foundation, either version 3 of the 8 | * License, or (at your option) any later version. 9 | * 10 | * envelope is distributed in the hope that it will be 11 | * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 13 | * Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with envelope. If not, see http://www.gnu.org/licenses/. 17 | */ 18 | 19 | namespace Envelope.Widget { 20 | public class CellRendererTextCompletion : Gtk.CellRendererText { 21 | public Gtk.ListStore store { get; set; } 22 | public int text_column { get; set; } 23 | 24 | private string path; 25 | private Gtk.Entry text_entry; 26 | 27 | public CellRendererTextCompletion () { 28 | Object (); 29 | build_ui (); 30 | connect_signals (); 31 | } 32 | 33 | private void build_ui () { 34 | text_entry = new Gtk.Entry (); 35 | } 36 | 37 | private void connect_signals () { 38 | text_entry.editing_done.connect (done_editing); 39 | } 40 | 41 | private void done_editing () { 42 | edited (path, text_entry.text); 43 | } 44 | 45 | public override unowned Gtk.CellEditable ? start_editing (Gdk.Event ? event, 46 | Gtk.Widget widget, 47 | string path, 48 | Gdk.Rectangle background_area, 49 | Gdk.Rectangle cell_area, 50 | Gtk.CellRendererState flags) { 51 | assert (editable); 52 | 53 | // create new completion every time, since the backing store might 54 | // have changed since last time 55 | var entry_completion = new Gtk.EntryCompletion (); 56 | entry_completion.set_model (store); 57 | entry_completion.set_text_column (text_column); 58 | 59 | text_entry.completion = entry_completion; 60 | 61 | this.path = path; 62 | text_entry.show (); 63 | text_entry.grab_focus (); 64 | 65 | return text_entry; 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/widget/CellRendererUpdatable.vala: -------------------------------------------------------------------------------- 1 | /* Copyright 2014 Nicolas Laplante 2 | * 3 | * This file is part of envelope. 4 | * 5 | * envelope is free software: you can redistribute it 6 | * and/or modify it under the terms of the GNU General Public License as 7 | * published by the Free Software Foundation, either version 3 of the 8 | * License, or (at your option) any later version. 9 | * 10 | * envelope is distributed in the hope that it will be 11 | * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 13 | * Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with envelope. If not, see http://www.gnu.org/licenses/. 17 | */ 18 | 19 | namespace Envelope.Widget { 20 | /** 21 | * The CellRendererUpdatable class is meant to be used inside a CellRendererPopoverContainer. 22 | * It allows to use multiple UIs inside a single CellRendererPopoverContainer. This can 23 | * be useful when the UI to show in a popover has to change depending on the row it 24 | * currently points to. 25 | * 26 | * In Envelope, it is used to show a popover on categories in the sidebar. 27 | */ 28 | public abstract class CellRendererUpdatable : Gtk.Grid { 29 | /** 30 | * Updates the UI of this instance 31 | */ 32 | public abstract void update (); 33 | 34 | /** 35 | * Signal which is emitted when this instance is dismissed 36 | */ 37 | public signal void dismiss (); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /tests/QIFImporterTest.vala: -------------------------------------------------------------------------------- 1 | using Envelope.Service; 2 | using Gee; 3 | 4 | namespace Envelope.Tests.Service.Importer.QIF { 5 | public void test_import_success () { 6 | try { 7 | ArrayList? transactions = QIFImporter.get_default ().import (test_data_path ("sample.qif")); 8 | 9 | assert_nonnull (transactions); 10 | assert_true (transactions.size == 29); 11 | } catch (ServiceError err) { 12 | Test.fail (); 13 | } catch (ImporterError err) { 14 | Test.fail (); 15 | } 16 | } 17 | 18 | public void test_import_file_not_found () { 19 | try { 20 | ArrayList? transactions = QIFImporter.get_default ().import (test_data_path ("enoent")); 21 | Test.fail (); 22 | } catch (ServiceError err) { 23 | assert (err is ServiceError.ENOENT); 24 | } catch (ImporterError err) { 25 | Test.fail (); 26 | } 27 | } 28 | 29 | private string test_data_path (string filename) { 30 | return Test.build_filename (Test.FileType.DIST, ".", filename); 31 | } 32 | } 33 | 34 | void main (string[] args) { 35 | Test.init (ref args); 36 | 37 | Test.add_func ("/envelope/service/importer/qif/file_not_found", 38 | Envelope.Tests.Service.Importer.QIF.test_import_file_not_found); 39 | 40 | Test.add_func ("/envelope/service/importer/qif/success", 41 | Envelope.Tests.Service.Importer.QIF.test_import_success); 42 | 43 | Test.run (); 44 | } 45 | -------------------------------------------------------------------------------- /tests/meson.build: -------------------------------------------------------------------------------- 1 | message ('Configuring tests…') 2 | 3 | configure_file ( 4 | input: 'sample.qif', 5 | output: 'sample.qif', 6 | copy: true 7 | ) 8 | 9 | importT = executable ( 10 | 'importTest', 11 | meson.source_root () + '/src/vapi/monetary.vapi', 12 | meson.source_root () + '/src/util/String.vala', 13 | meson.source_root () + '/src/util/Date.vala', 14 | 15 | meson.source_root () + '/src/model/Account.vala', 16 | meson.source_root () + '/src/model/Transaction.vala', 17 | meson.source_root () + '/src/model/Category.vala', 18 | 19 | meson.source_root () + '/src/service/Error.vala', 20 | meson.source_root () + '/src/service/Importer.vala', 21 | meson.source_root () + '/src/service/QIFImporter.vala', 22 | 23 | 'QIFImporterTest.vala', 24 | 25 | dependencies: [ 26 | dependency ('glib-2.0', version: '>= 2.29.0'), 27 | dependency ('gee-0.8'), 28 | dependency ('gio-2.0'), 29 | ], 30 | install: false 31 | ) 32 | 33 | test ('Import QIF files', importT) -------------------------------------------------------------------------------- /tests/sample.qif: -------------------------------------------------------------------------------- 1 | !Type:Bank 2 | D1/1/2014 3 | T1500 4 | PPaycheck 5 | LIncome.Salary 6 | ^ 7 | D2/1/2014 8 | T1500 9 | PPaycheck 10 | LIncome.Salary 11 | ^ 12 | D3/1/2014 13 | T1500 14 | PPaycheck 15 | LIncome.Salary 16 | ^ 17 | D4/1/2014 18 | T1500 19 | PPaycheck 20 | LIncome.Salary 21 | ^ 22 | D5/1/2014 23 | T1500 24 | PPaycheck 25 | LIncome.Salary 26 | ^ 27 | D6/1/2014 28 | T1500 29 | PPaycheck 30 | LIncome.Salary 31 | ^ 32 | D7/1/2014 33 | T1500 34 | PPaycheck 35 | LIncome.Salary 36 | ^ 37 | D8/1/2014 38 | T1500 39 | PPaycheck 40 | LIncome.Salary 41 | ^ 42 | D9/1/2014 43 | T1500 44 | PPaycheck 45 | LIncome.Salary 46 | ^ 47 | D10/1/2014 48 | T1500 49 | PPaycheck 50 | LIncome.Salary 51 | ^ 52 | D11/1/2014 53 | T1500 54 | PPaycheck 55 | LIncome.Salary 56 | ^ 57 | D12/1/2014 58 | T1500 59 | PPaycheck 60 | LIncome.Salary 61 | ^ 62 | D4/15/2014 63 | T1500 64 | PBonus Paycheck 65 | LIncome.Bonus 66 | ^ 67 | D10/25/2014 68 | T4000 69 | PBonus Paycheck 70 | LIncome.Bonus 71 | ^ 72 | D3/9/2014 73 | T50 74 | PGift from Grandma 75 | LIncome.Gifts Received 76 | ^ 77 | D8/4/2014 78 | T150 79 | PGift from Grandma 80 | LIncome.Gifts Received 81 | ^ 82 | D10/10/2014 83 | T50 84 | PGift from Grandma 85 | LIncome.Gifts Received 86 | ^ 87 | D1/20/2014 88 | T-98 89 | PPayment to Credit Card 90 | LLiabilities.Credit Card 91 | ^ 92 | D2/17/2014 93 | T-134 94 | PPayment to Credit Card 95 | LLiabilities.Credit Card 96 | ^ 97 | D3/18/2014 98 | T-245 99 | PPayment to Credit Card 100 | LLiabilities.Credit Card 101 | ^ 102 | D4/11/2014 103 | T-147 104 | PPayment to Credit Card 105 | LLiabilities.Credit Card 106 | ^ 107 | D5/16/2014 108 | T-178 109 | PPayment to Credit Card 110 | LLiabilities.Credit Card 111 | ^ 112 | D6/17/2014 113 | T-109 114 | PPayment to Credit Card 115 | LLiabilities.Credit Card 116 | ^ 117 | D7/12/2014 118 | T-245 119 | PPayment to Credit Card 120 | LLiabilities.Credit Card 121 | ^ 122 | D8/10/2014 123 | T-231 124 | PPayment to Credit Card 125 | LLiabilities.Credit Card 126 | ^ 127 | D9/7/2014 128 | T-289 129 | PPayment to Credit Card 130 | LLiabilities.Credit Card 131 | ^ 132 | D10/15/2014 133 | T-138 134 | PPayment to Credit Card 135 | LLiabilities.Credit Card 136 | ^ 137 | D11/17/2014 138 | T-252 139 | PPayment to Credit Card 140 | LLiabilities.Credit Card 141 | ^ 142 | D12/10/2014 143 | T-335 144 | PPayment to Credit Card 145 | LLiabilities.Credit Card 146 | ^ 147 | --------------------------------------------------------------------------------