├── .gitignore ├── LICENSE ├── README.md ├── _config.yml ├── bin ├── Kalendar-beta-1.2.1-win32.rar ├── Kalendar-stable-2.8.2-x86_64 └── tools │ ├── Add Facebook Event.sh │ └── FBEventTool.jar ├── icon ├── kalendar.png ├── kalendar.xcf └── kalendar_circle.png ├── screencast.gif ├── screenshot.png ├── screenshot2.png └── src ├── Kalendar.pro ├── Kalendar.pro.user.3148a8b ├── main.cpp ├── model ├── category.h ├── date.h └── event.h ├── persistence ├── pmanager.cpp ├── pmanager.h ├── securepmanager.cpp └── securepmanager.h ├── test ├── Makefile ├── main.cpp ├── persistence │ ├── pmanager_test.cpp │ └── pmanager_test.h ├── test.cpp ├── test.h └── util │ ├── dateutil_test.cpp │ ├── dateutil_test.h │ ├── eventutil_test.cpp │ └── eventutil_test.h ├── tools ├── .classpath ├── .project ├── .settings │ └── org.eclipse.jdt.core.prefs └── fbeventplugin │ ├── FBEvent.java │ ├── FBParser.java │ └── MainWindow.java ├── util ├── dateutil.cpp ├── dateutil.h ├── eventutil.cpp ├── eventutil.h ├── linuxnotifymanager.cpp ├── linuxnotifymanager.h ├── notifymanager.h ├── pluginmanager.cpp └── pluginmanager.h └── view ├── categorydialog.cpp ├── categorydialog.h ├── categoryeditdialog.cpp ├── categoryeditdialog.h ├── categoryselectdialog.cpp ├── categoryselectdialog.h ├── customdialog.cpp ├── customdialog.h ├── eventdialog.cpp ├── eventdialog.h ├── monthview.cpp ├── monthview.h ├── qframe_extended.h ├── qlabel_event.cpp ├── qlabel_event.h ├── qpushbutton_extended.cpp ├── qpushbutton_extended.h ├── qwidget_extended.cpp ├── qwidget_extended.h ├── settings.cpp ├── settings.h ├── ui_categorydialog.h ├── ui_categoryeditdialog.h ├── ui_categoryselectdialog.h ├── ui_customdialog.h ├── ui_eventdialog.h ├── ui_kalendar.h ├── ui_monthview.h ├── ui_settings.h └── view.h /.gitignore: -------------------------------------------------------------------------------- 1 | src/Kalendar.pro.user 2 | *.gch 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | {one line to give the program's name and a brief idea of what it does.} 635 | Copyright (C) {year} {name of author} 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | {project} Copyright (C) {year} {fullname} 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Kalendar 2 | 3 | ![project status](https://img.shields.io/badge/project%20status-active-brightgreen.svg) 4 | 5 | Kalendar is a cross-platform Gregorian calendar application written in C++ with the Qt5 library for the gui. This project **is not related** with KDE. I'm trying to keep Kalendar as simple as possible and I want to avoid annoying dependencies (so you can easly install it everywhere). This calendar is offline and it doesn't support synchronization with online calendars at the moment. 6 | 7 | The project was inspired by gnome-calendar, but I'm writing it from scratch. 8 | 9 | FEATURES: 10 | * Support multiple calendars (i.e. databases) 11 | * Manage your events 12 | * Manage your TODOs 13 | * Notify future events 14 | * iCal format support 15 | * External tools support (e.g. Facebook plugin) 16 | * Desktop Environment independent 17 | 18 | TODO: 19 | * External plugins support 20 | * Year view for long-term events 21 | * Add an encryption layer 22 | * Improve support for recurrent events 23 | * Implement search function 24 | 25 | *Warning:* This application isn't compatible with 32 bit systems (see: [year 2038 problem](https://en.wikipedia.org/wiki/Year_2038_problem) ), but you can make it compatible adding `#define BIT32` inside `event.h`. 26 | 27 | ### Screenshot 28 | 29 | Screenshot from Kalendar-stable-2.7 30 | ![screenshot](https://raw.githubusercontent.com/echo-devim/kalendar/master/screenshot2.png) 31 | 32 | Screenshot from Kalendar-stable-2.0 33 | ![screenshot](https://raw.githubusercontent.com/echo-devim/kalendar/master/screenshot.png) 34 | 35 | ### Screencast 36 | 37 | Screencast from Kalendar-alpha-1.3-x86\_64 (**OLD**) 38 | ![screencast](https://raw.githubusercontent.com/echo-devim/kalendar/master/screencast.gif) 39 | 40 | ### Motivations 41 | I need to schedule my time in a more efficient way. I don't like online calendars like google calendar for the privacy issues. I looked for some good calendar applications, but there are just a couple of such calendars. This is a shame, because linux exists from so many years and the users need to have a good calendar application. The two main calendar applications are: gnome-desktop and korganizer. Their problems are a lot of dependencies and complicated compilation. So, I decided to develop a simple but powerful calendar with painless compilation, without dependencies and desktop environment (almost) independent. 42 | 43 | ### Compilation 44 | 45 | You need to have `qmake`. In Fedora you can find it inside the `qt-devel` package. 46 | 47 | You need to install also `sqlite3` developer package. In Fedora the package is called `sqlite-devel`. 48 | 49 | After you've installed these packages, you can run `qmake` (or `qmake-qt5` in Fedora) inside the src folder and then the `make` command. 50 | You should get the Kalendar executable inside the same folder. 51 | 52 | Note: Kalendar will create the following folder: ~/kalendar, so remember to delete it if you decide to delete Kalendar. 53 | 54 | ### Further Notes 55 | 56 | In the `tools` folder, you can put **sh** or **bat** scripts that will be integrated in the tools menu of the application. The plugins at the moment are only external tools. They require to run `kalendar` without the absolute path, so (on linux) you should create the script `/bin/kalendar` with the following content: 57 | 58 | ``` 59 | #! /bin/bash 60 | cd /absolute/path/to/the/bin/directory/ 61 | ./Kalendar-version "$@" 62 | ``` 63 | Remember to give the right permissions with `# chmod +x /bin/kalendar`. To see the effects of the plugins you may need to restart the application or refresh the view. 64 | If you create a desktop file, use `kalendar` for the **Exec** field. 65 | 66 | ### Tips 67 | 68 | If you right-click on an event, it'll be deleted. If you middle-click on an event, it'll be selected. Once an event is selected, you can move it with A (backward) or F (forward) keys and resize it using S and D keys. Press Canc to cancel the selection. 69 | Press **c** to easily switch among your calendars. 70 | 71 | ### License 72 | GPLv3+ 73 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-tactile -------------------------------------------------------------------------------- /bin/Kalendar-beta-1.2.1-win32.rar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/echo-devim/kalendar/f9bbb2d96300503476fb8110d7be6c2cc5c3cf80/bin/Kalendar-beta-1.2.1-win32.rar -------------------------------------------------------------------------------- /bin/Kalendar-stable-2.8.2-x86_64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/echo-devim/kalendar/f9bbb2d96300503476fb8110d7be6c2cc5c3cf80/bin/Kalendar-stable-2.8.2-x86_64 -------------------------------------------------------------------------------- /bin/tools/Add Facebook Event.sh: -------------------------------------------------------------------------------- 1 | SCRIPTPATH="$( cd "$(dirname "$0")" ; pwd -P )" 2 | java -jar $SCRIPTPATH/FBEventTool.jar 3 | -------------------------------------------------------------------------------- /bin/tools/FBEventTool.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/echo-devim/kalendar/f9bbb2d96300503476fb8110d7be6c2cc5c3cf80/bin/tools/FBEventTool.jar -------------------------------------------------------------------------------- /icon/kalendar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/echo-devim/kalendar/f9bbb2d96300503476fb8110d7be6c2cc5c3cf80/icon/kalendar.png -------------------------------------------------------------------------------- /icon/kalendar.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/echo-devim/kalendar/f9bbb2d96300503476fb8110d7be6c2cc5c3cf80/icon/kalendar.xcf -------------------------------------------------------------------------------- /icon/kalendar_circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/echo-devim/kalendar/f9bbb2d96300503476fb8110d7be6c2cc5c3cf80/icon/kalendar_circle.png -------------------------------------------------------------------------------- /screencast.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/echo-devim/kalendar/f9bbb2d96300503476fb8110d7be6c2cc5c3cf80/screencast.gif -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/echo-devim/kalendar/f9bbb2d96300503476fb8110d7be6c2cc5c3cf80/screenshot.png -------------------------------------------------------------------------------- /screenshot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/echo-devim/kalendar/f9bbb2d96300503476fb8110d7be6c2cc5c3cf80/screenshot2.png -------------------------------------------------------------------------------- /src/Kalendar.pro: -------------------------------------------------------------------------------- 1 | #------------------------------------------------- 2 | # 3 | # Project created by QtCreator 2016-03-09T17:50:52 4 | # 5 | #------------------------------------------------- 6 | 7 | QT += core gui 8 | 9 | greaterThan(QT_MAJOR_VERSION, 4): QT += widgets 10 | 11 | TARGET = Kalendar 12 | TEMPLATE = app 13 | 14 | 15 | SOURCES += main.cpp\ 16 | persistence/pmanager.cpp \ 17 | test/test.cpp \ 18 | test/persistence/pmanager_test.cpp \ 19 | view/monthview.cpp \ 20 | util/dateutil.cpp \ 21 | test/util/dateutil_test.cpp \ 22 | view/eventdialog.cpp \ 23 | view/qwidget_extended.cpp \ 24 | view/qlabel_event.cpp \ 25 | view/categorydialog.cpp \ 26 | view/qpushbutton_extended.cpp \ 27 | view/customdialog.cpp \ 28 | view/categoryeditdialog.cpp \ 29 | view/categoryselectdialog.cpp \ 30 | util/linuxnotifymanager.cpp \ 31 | persistence/securepmanager.cpp \ 32 | util/eventutil.cpp \ 33 | test/util/eventutil_test.cpp \ 34 | util/pluginmanager.cpp \ 35 | view/settings.cpp 36 | 37 | HEADERS += model/event.h \ 38 | persistence/pmanager.h \ 39 | model/category.h \ 40 | test/test.h \ 41 | test/persistence/pmanager_test.h \ 42 | model/category.h \ 43 | model/event.h \ 44 | view/monthview.h \ 45 | view/qframe_extended.h \ 46 | model/date.h \ 47 | util/dateutil.h \ 48 | test/util/dateutil_test.h \ 49 | view/eventdialog.h \ 50 | view/qwidget_extended.h \ 51 | view/qlabel_event.h \ 52 | view/view.h \ 53 | view/categorydialog.h \ 54 | view/qpushbutton_extended.h \ 55 | view/customdialog.h \ 56 | view/categoryeditdialog.h \ 57 | view/categoryselectdialog.h \ 58 | util/notifymanager.h \ 59 | util/linuxnotifymanager.h \ 60 | persistence/securepmanager.h \ 61 | util/eventutil.h \ 62 | test/util/eventutil_test.h \ 63 | util/pluginmanager.h \ 64 | view/settings.h 65 | 66 | QMAKE_LIBS += -lsqlite3 67 | QMAKE_LIBS += -lstdc++fs 68 | QMAKE_CXXFLAGS += -std=c++11 69 | 70 | #For DEBUG purpose 71 | #QMAKE_CXXFLAGS += -g 72 | -------------------------------------------------------------------------------- /src/Kalendar.pro.user.3148a8b: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | EnvironmentId 7 | {3148a8be-7f16-4f3b-be24-d7503ad26e51} 8 | 9 | 10 | ProjectExplorer.Project.ActiveTarget 11 | 0 12 | 13 | 14 | ProjectExplorer.Project.EditorSettings 15 | 16 | true 17 | false 18 | true 19 | 20 | Cpp 21 | 22 | CppGlobal 23 | 24 | 25 | 26 | QmlJS 27 | 28 | QmlJSGlobal 29 | 30 | 31 | 2 32 | UTF-8 33 | false 34 | 4 35 | false 36 | 80 37 | true 38 | true 39 | 1 40 | true 41 | false 42 | 0 43 | true 44 | true 45 | 0 46 | 8 47 | true 48 | 1 49 | true 50 | true 51 | true 52 | false 53 | 54 | 55 | 56 | ProjectExplorer.Project.PluginSettings 57 | 58 | 59 | 60 | ProjectExplorer.Project.Target.0 61 | 62 | Desktop 63 | Desktop 64 | {7f49c389-e746-4569-bc59-db60707f3f5a} 65 | 0 66 | 0 67 | 0 68 | 69 | /home/greg/Scrivania/Varie/qt/build-Kalendar-Desktop-Debug 70 | 71 | 72 | true 73 | qmake 74 | 75 | QtProjectManager.QMakeBuildStep 76 | true 77 | 78 | false 79 | false 80 | false 81 | 82 | 83 | true 84 | Make 85 | 86 | Qt4ProjectManager.MakeStep 87 | 88 | -w 89 | -r 90 | 91 | false 92 | 93 | 94 | 95 | 2 96 | Build 97 | 98 | ProjectExplorer.BuildSteps.Build 99 | 100 | 101 | 102 | true 103 | Make 104 | 105 | Qt4ProjectManager.MakeStep 106 | 107 | -w 108 | -r 109 | 110 | true 111 | clean 112 | 113 | 114 | 1 115 | Clean 116 | 117 | ProjectExplorer.BuildSteps.Clean 118 | 119 | 2 120 | false 121 | 122 | Debug 123 | 124 | Qt4ProjectManager.Qt4BuildConfiguration 125 | 2 126 | true 127 | 128 | 129 | /home/greg/Scrivania/Varie/qt/build-Kalendar-Desktop-Release 130 | 131 | 132 | true 133 | qmake 134 | 135 | QtProjectManager.QMakeBuildStep 136 | false 137 | 138 | false 139 | false 140 | false 141 | 142 | 143 | true 144 | Make 145 | 146 | Qt4ProjectManager.MakeStep 147 | 148 | -w 149 | -r 150 | 151 | false 152 | 153 | 154 | 155 | 2 156 | Build 157 | 158 | ProjectExplorer.BuildSteps.Build 159 | 160 | 161 | 162 | true 163 | Make 164 | 165 | Qt4ProjectManager.MakeStep 166 | 167 | -w 168 | -r 169 | 170 | true 171 | clean 172 | 173 | 174 | 1 175 | Clean 176 | 177 | ProjectExplorer.BuildSteps.Clean 178 | 179 | 2 180 | false 181 | 182 | Release 183 | 184 | Qt4ProjectManager.Qt4BuildConfiguration 185 | 0 186 | true 187 | 188 | 189 | /home/greg/Scrivania/Varie/qt/build-Kalendar-Desktop-Profile 190 | 191 | 192 | true 193 | qmake 194 | 195 | QtProjectManager.QMakeBuildStep 196 | true 197 | 198 | false 199 | true 200 | false 201 | 202 | 203 | true 204 | Make 205 | 206 | Qt4ProjectManager.MakeStep 207 | 208 | -w 209 | -r 210 | 211 | false 212 | 213 | 214 | 215 | 2 216 | Build 217 | 218 | ProjectExplorer.BuildSteps.Build 219 | 220 | 221 | 222 | true 223 | Make 224 | 225 | Qt4ProjectManager.MakeStep 226 | 227 | -w 228 | -r 229 | 230 | true 231 | clean 232 | 233 | 234 | 1 235 | Clean 236 | 237 | ProjectExplorer.BuildSteps.Clean 238 | 239 | 2 240 | false 241 | 242 | Profile 243 | 244 | Qt4ProjectManager.Qt4BuildConfiguration 245 | 0 246 | true 247 | 248 | 3 249 | 250 | 251 | 0 252 | Deploy 253 | 254 | ProjectExplorer.BuildSteps.Deploy 255 | 256 | 1 257 | Deploy locally 258 | 259 | ProjectExplorer.DefaultDeployConfiguration 260 | 261 | 1 262 | 263 | 264 | false 265 | false 266 | 1000 267 | 268 | true 269 | 270 | false 271 | false 272 | false 273 | false 274 | true 275 | 0.01 276 | 10 277 | true 278 | 1 279 | 25 280 | 281 | 1 282 | true 283 | false 284 | true 285 | valgrind 286 | 287 | 0 288 | 1 289 | 2 290 | 3 291 | 4 292 | 5 293 | 6 294 | 7 295 | 8 296 | 9 297 | 10 298 | 11 299 | 12 300 | 13 301 | 14 302 | 303 | 2 304 | 305 | Kalendar 306 | Kalendar2 307 | Qt4ProjectManager.Qt4RunConfiguration:/home/greg/Scrivania/Varie/qt/Kalendar/src/Kalendar.pro 308 | true 309 | 310 | Kalendar.pro 311 | false 312 | false 313 | 314 | /home/greg/Scrivania/Varie/qt/build-Kalendar-Desktop-Debug 315 | 3768 316 | false 317 | true 318 | false 319 | false 320 | true 321 | 322 | 1 323 | 324 | 325 | 326 | ProjectExplorer.Project.TargetCount 327 | 1 328 | 329 | 330 | ProjectExplorer.Project.Updater.FileVersion 331 | 18 332 | 333 | 334 | Version 335 | 18 336 | 337 | 338 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include "view/monthview.h" 2 | #include "persistence/securepmanager.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "test/test.h" 9 | #include "util/linuxnotifymanager.h" 10 | #include "util/eventutil.h" 11 | 12 | #define RUN_TESTS 0 13 | 14 | int main(int argc, char *argv[]) 15 | { 16 | QApplication a(argc, argv); 17 | int ret = 0; 18 | #if RUN_TESTS 19 | Test t; 20 | t.test_persistence(); 21 | t.test_util(); 22 | #else 23 | bool cli = false; 24 | QCommandLineParser parser; 25 | parser.addHelpOption(); 26 | parser.addOptions({ 27 | {{"n", "notify"}, 28 | QCoreApplication::translate("main", "Notify the events in the future starting from today"), 29 | QCoreApplication::translate("main", "notifynextdays")}, 30 | {{"a", "add"}, 31 | QCoreApplication::translate("main", "Add an event"), 32 | QCoreApplication::translate("main", "event")}, 33 | {{"d", "delete"}, 34 | QCoreApplication::translate("main", "Delete an event"), 35 | QCoreApplication::translate("main", "event")}, 36 | }); 37 | parser.process(a); 38 | if ((cli = parser.isSet("add"))) { 39 | QString event = parser.value("add"); 40 | SecurePManager spm; 41 | spm.add_event(EventUtil::parseString(event.toStdString())); 42 | } else if((cli = parser.isSet("delete"))) { 43 | /* TODO: implement deletion by id */ 44 | QString eventName = parser.value("delete"); 45 | SecurePManager spm; 46 | for (Event *e : spm.get_all_events()) { 47 | if (e->getName() == eventName.toStdString()) 48 | spm.remove_event(e); 49 | } 50 | } else if ((cli = parser.isSet("notify"))) { 51 | /* Show notifications about the events in the next days */ 52 | QString notify = parser.value("notify"); 53 | LinuxNotifyManager nm; 54 | if (!nm.notifyEvents(notify.toInt())) { 55 | printf("Error in notifyEvents"); 56 | ret = 1; 57 | } 58 | } 59 | MonthView window; 60 | if (!cli) { 61 | window.show(); 62 | ret = a.exec(); 63 | } 64 | #endif 65 | return ret; 66 | } 67 | -------------------------------------------------------------------------------- /src/model/category.h: -------------------------------------------------------------------------------- 1 | #ifndef CATEGORY_H 2 | #define CATEGORY_H 3 | 4 | using namespace std; 5 | 6 | class Category 7 | { 8 | private: 9 | unsigned int id; 10 | string name; 11 | string color; 12 | 13 | public: 14 | Category (Category &category) { 15 | this->name = category.getName(); 16 | this->color = category.getColor(); 17 | this->id = category.getId(); 18 | } 19 | 20 | Category(unsigned int id, const string &name, const string &color) { 21 | this->name = name; 22 | this->color = color; 23 | if (id == 0) 24 | this->id = static_cast (hash()(this->name + this->color)); 25 | else 26 | this->id = id; 27 | } 28 | 29 | ~Category() { 30 | } 31 | 32 | string getName() { return name; } 33 | string getColor() { return color; } 34 | unsigned int getId() { return id; } 35 | 36 | bool equals(Category &c) { 37 | return (this->id == c.getId()); 38 | } 39 | 40 | long hashcode() { 41 | return static_cast (this->id); 42 | } 43 | }; 44 | 45 | #endif // CATEGORY_H 46 | -------------------------------------------------------------------------------- /src/model/date.h: -------------------------------------------------------------------------------- 1 | #ifndef DATE_H 2 | #define DATE_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | using namespace std; 9 | 10 | class Date { 11 | private: 12 | int mday; //month day 13 | int wday; //week day 14 | int month; 15 | int year; 16 | public: 17 | Date() {} 18 | 19 | Date(int mday, int wday, int month, int year) { 20 | this->mday = mday; 21 | this->wday = wday; 22 | this->month = month; 23 | this->year = year; 24 | } 25 | int getMonthDay() { return mday; } 26 | int getWeekDay() { return wday; } 27 | int getMonth() { return month; } 28 | int getYear() { return year; } 29 | void setMonthDay(int mday) { this->mday = mday; } 30 | void setWeekDay(int wday) { this->wday = wday; } 31 | void setMonth(int month) { this->month = month; } 32 | void setYear(int year) { this->year = year; } 33 | //TODO: maybe it can be useful to add the method: isHoliday() 34 | 35 | int compareTo (Date &d) { 36 | int ret; 37 | ret = this->year - d.getYear(); 38 | if (ret != 0) return ret; 39 | else { //Same year, compare months 40 | ret = this->month - d.getMonth(); 41 | if (ret != 0) return ret; 42 | else { //Same year and month, compare days 43 | ret = this->mday - d.getMonthDay(); 44 | return ret; 45 | } 46 | } 47 | } 48 | 49 | Date& operator=(Date d) { 50 | this->mday = d.getMonthDay(); 51 | this->wday = d.getWeekDay(); 52 | this->month = d.getMonth(); 53 | this->year = d.getYear(); 54 | return *this; 55 | } 56 | 57 | bool operator==(Date &d) { 58 | return (this->mday == d.getMonthDay()) && 59 | (this->wday == d.getWeekDay()) && 60 | (this->month == d.getMonth()) && 61 | (this->year == d.getYear()); 62 | } 63 | 64 | string toString(bool weekday) { 65 | /* In this project the standard format for dates is: d/m/y */ 66 | char sdate[11]; 67 | snprintf(sdate, 11, "%02d/%02d/%d", this->mday, this->month, this->year); 68 | string ret (sdate); 69 | if (weekday) 70 | ret += " Week-day: " + to_string(this->wday); 71 | return ret; 72 | } 73 | }; 74 | 75 | #endif 76 | -------------------------------------------------------------------------------- /src/model/event.h: -------------------------------------------------------------------------------- 1 | #ifndef EVENT_H 2 | #define EVENT_H 3 | 4 | #include 5 | 6 | #include "category.h" 7 | 8 | /* A todo is defined as an event with special dates, they are the following: 9 | * start = end = 29/01/2105 09:40 , the UTC timestamp is: 4262665200 10 | * Note: this value is incompatible for 32bit systems due to the 2038 year bug (Reference: https://en.wikipedia.org/wiki/Year_2038_problem) 11 | * The todo is only an high-level concept managed by the logic view, thus the persistence part consider them as normal events. */ 12 | //#define BIT32 13 | #ifndef BIT32 14 | // As explained above, this is for 64 bit systems 15 | #define TODO_DATE 4262665200 16 | #else 17 | // 2147437133 is the 18 Jan 2038, the maximum value representable with a 32 bit system 18 | #define TODO_DATE 2147437133 19 | #endif 20 | 21 | using namespace std; 22 | 23 | class Event 24 | { 25 | private: 26 | unsigned int id; 27 | string name; 28 | string description; 29 | string place; 30 | Category *category; 31 | /* Timestamp */ 32 | time_t start; 33 | time_t end; 34 | 35 | public: 36 | Event(Event &event) { 37 | this->name = event.getName(); 38 | this->description = event.getDescription(); 39 | this->place = event.getPlace(); 40 | this->category = new Category(*event.getCategory()); 41 | this->id = event.getId(); 42 | this->start = event.getStart(); 43 | this->end = event.getEnd(); 44 | } 45 | 46 | Event(unsigned int id, string name, const string &description, const string &place, Category *category, time_t start, time_t end) { 47 | this->name = name; 48 | this->description = description; 49 | this->place = place; 50 | if (category == NULL) { 51 | /* An event with a NULL category is inconsistent, it shouldn't exist */ 52 | this->category = NULL; 53 | fprintf(stderr, "Event %d (%s) received NULL category.\n", id, name.c_str()); 54 | } else 55 | this->category = new Category(*category); 56 | this->start = start; 57 | this->end = end; 58 | if (id == 0) 59 | /* The returned value from the hash function could be bigger than an integer, so be careful with normal integers. 60 | * I use an unsigned integer to have always a positive number (also with the overflow). */ 61 | this->id = static_cast (hash()(this->name + this->description + this->place)) + (this->category ? this->category->getId() : 0) + static_cast ((this->start / 1000) + (this->end - this->start)); 62 | else 63 | this->id = id; 64 | } 65 | 66 | ~Event() { 67 | if (this->category != NULL) delete this->category; 68 | } 69 | 70 | void setInvalid() { 71 | this->id = 65535; 72 | } 73 | 74 | bool isInvalid() { 75 | return this->id == 65535; 76 | } 77 | 78 | unsigned int getId() { return id; } 79 | string getName() { return name; } 80 | string getDescription() { return description; } 81 | string getPlace() { return place; } 82 | Category *getCategory() { return category; } 83 | time_t getStart() { return start; } 84 | time_t getEnd() { return end; } 85 | 86 | bool equals(Event &e) { 87 | return (this->id == e.getId()); 88 | } 89 | 90 | long hashcode() { 91 | return this->id; 92 | } 93 | }; 94 | 95 | #endif // EVENT_H 96 | -------------------------------------------------------------------------------- /src/persistence/pmanager.cpp: -------------------------------------------------------------------------------- 1 | #include "pmanager.h" 2 | 3 | #include 4 | 5 | PManager::PManager(string database) 6 | { 7 | if (database != DEFAULT_DATABASE_NAME) db_name = database; 8 | init_db(db_name); 9 | } 10 | 11 | PManager::~PManager() { 12 | sqlite3_close(this->db); 13 | } 14 | 15 | string PManager::get_db_folder() { 16 | return this->db_folder; 17 | } 18 | 19 | void PManager::init_db(string db_name) { 20 | /* Close an already open database */ 21 | if (this->db != NULL) { 22 | sqlite3_close(this->db); 23 | } 24 | /* Open the database (will be created if it doesn't exist) */ 25 | this->db_folder = string(getpwuid(getuid())->pw_dir) + string("/" FOLDER_NAME "/"); 26 | this->db_path = this->db_folder + string(db_name); 27 | ifstream dbfile(this->db_path.c_str()); 28 | bool db_not_exists = !dbfile; 29 | if (db_not_exists) { 30 | mkdir((string(getpwuid(getuid())->pw_dir) + string("/" FOLDER_NAME)).c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); 31 | ofstream new_dbfile(this->db_path.c_str()); 32 | new_dbfile.close(); 33 | } 34 | 35 | int rc = sqlite3_open(this->db_path.c_str(), &this->db); 36 | char *err_msg = 0; 37 | 38 | if (rc != SQLITE_OK) { 39 | 40 | fprintf(stderr, "Cannot open database: %s\n", sqlite3_errmsg(this->db)); 41 | sqlite3_free(err_msg); 42 | sqlite3_close(this->db); 43 | } 44 | 45 | if (db_not_exists) { 46 | const char *sql = "CREATE TABLE Categories(id UNSIGNED INTEGER PRIMARY KEY, name TEXT, color TEXT);" 47 | "CREATE TABLE Events(id UNSIGNED INTEGER PRIMARY KEY, name TEXT, description TEXT," 48 | "place TEXT, category UNSIGNED INTEGER, start DATETIME, end DATETIME, child UNSIGNED INTEGER," 49 | "FOREIGN KEY(category) REFERENCES Categories(id) ON DELETE RESTRICT," 50 | "FOREIGN KEY(child) REFERENCES Events(id) ON DELETE CASCADE);" 51 | "INSERT INTO Categories VALUES(1, 'Default', '#1022A0');" 52 | "PRAGMA foreign_keys = ON;"; 53 | 54 | rc = sqlite3_exec(this->db, sql, 0, 0, &err_msg); 55 | 56 | if (rc != SQLITE_OK ) { 57 | fprintf(stderr, "SQL error: %s\n", err_msg); 58 | sqlite3_free(err_msg); 59 | sqlite3_close(this->db); 60 | } 61 | } 62 | } 63 | 64 | void PManager::set_db(string database) { 65 | db_name = database; 66 | init_db(db_name); 67 | } 68 | 69 | string PManager::get_db_name() { 70 | return db_name; 71 | } 72 | 73 | vector PManager::get_db_list() { 74 | vector db_list; 75 | for (experimental::filesystem::directory_entry e : experimental::filesystem::directory_iterator(this->db_path.substr(0, this->db_path.find_last_of('/') ))) { 76 | experimental::filesystem::path p = e.path(); 77 | if (p.extension() == ".sql") 78 | db_list.push_back(p.filename()); 79 | } 80 | return db_list; 81 | } 82 | 83 | string PManager::filterSpecialChars(string str) { 84 | int start_pos = 0; 85 | string to = "’"; 86 | while((str.find('\'', start_pos)) != std::string::npos) { 87 | start_pos = str.find('\'', start_pos); 88 | str.replace(start_pos, 1, to); 89 | start_pos += to.length(); 90 | } 91 | return str; 92 | } 93 | 94 | bool PManager::add_event(Event *e, Event *child) { 95 | char *err_msg = 0; 96 | sqlite3_stmt *stmt; 97 | string filteredName, filteredDescription, filteredPlace; 98 | if ((e->getName().length() < 3) || (difftime(e->getStart(), e->getEnd()) > 0) || (e->getCategory() == NULL)) return false; 99 | int rc = sqlite3_prepare_v2(this->db, "INSERT INTO Events VALUES(?, ?, ?, ?, ?, ?, ?, ?);", -1, &stmt, NULL); 100 | if (rc != SQLITE_OK ) { 101 | fprintf(stderr, "SQL error in prepare: %s\n", sqlite3_errmsg(this->db)); 102 | sqlite3_free(err_msg); 103 | sqlite3_close(this->db); 104 | return false; 105 | } 106 | sqlite3_bind_int64 (stmt, 1, e->getId()); 107 | filteredName = filterSpecialChars(e->getName()); 108 | sqlite3_bind_text(stmt, 2, filteredName.c_str(), filteredName.length(), 0); 109 | filteredDescription = filterSpecialChars(e->getDescription()); 110 | sqlite3_bind_text(stmt, 3, filteredDescription.c_str(), filteredDescription.length(), 0); 111 | filteredPlace = filterSpecialChars(e->getPlace()); 112 | sqlite3_bind_text(stmt, 4, filteredPlace.c_str(), filteredPlace.length(), 0); 113 | sqlite3_bind_int64(stmt, 5, e->getCategory()->getId()); 114 | sqlite3_bind_int64(stmt, 6, e->getStart()); 115 | sqlite3_bind_int64(stmt, 7, e->getEnd()); 116 | if (child != NULL) 117 | sqlite3_bind_int64(stmt, 8, child->getId()); 118 | else 119 | sqlite3_bind_null(stmt, 8); 120 | //commit 121 | rc = sqlite3_step(stmt); 122 | if (rc != SQLITE_DONE ) { 123 | fprintf(stderr, "SQL error in commit: %s\n", sqlite3_errmsg(this->db)); 124 | sqlite3_free(err_msg); 125 | return false; 126 | } 127 | //free memory 128 | sqlite3_finalize(stmt); 129 | return true; 130 | } 131 | 132 | bool PManager::replace_event(Event *old_event, Event *new_event) { 133 | char *err_msg = 0; 134 | sqlite3_stmt *stmt; 135 | string filteredName, filteredDescription, filteredPlace; 136 | if ((new_event->getName().length() < 3) || (difftime(new_event->getStart(), new_event->getEnd()) > 0) || (new_event->getCategory() == NULL)) return false; 137 | int rc = sqlite3_prepare_v2(this->db, "UPDATE Events SET id=?, name=?, description=?, place=?, category=?, start=?, end=? WHERE id=?;", -1, &stmt, NULL); 138 | if (rc != SQLITE_OK ) { 139 | fprintf(stderr, "SQL error in prepare: %s\n", sqlite3_errmsg(this->db)); 140 | sqlite3_free(err_msg); 141 | return false; 142 | } 143 | sqlite3_bind_int64(stmt, 1, new_event->getId()); 144 | filteredName = filterSpecialChars(new_event->getName()); 145 | sqlite3_bind_text(stmt, 2, filteredName.c_str(), filteredName.length(), 0); 146 | filteredDescription = filterSpecialChars(new_event->getDescription()); 147 | sqlite3_bind_text(stmt, 3, filteredDescription.c_str(), filteredDescription.length(), 0); 148 | filteredPlace = filterSpecialChars(new_event->getPlace()); 149 | sqlite3_bind_text(stmt, 4, filteredPlace.c_str(), filteredPlace.length(), 0); 150 | sqlite3_bind_int64(stmt, 5, new_event->getCategory()->getId()); 151 | sqlite3_bind_int64(stmt, 6, new_event->getStart()); 152 | sqlite3_bind_int64(stmt, 7, new_event->getEnd()); 153 | sqlite3_bind_int64(stmt, 8, old_event->getId()); 154 | //commit 155 | rc = sqlite3_step(stmt); 156 | if (rc != SQLITE_DONE ) { 157 | fprintf(stderr, "SQL error in commit: %s\n", sqlite3_errmsg(this->db)); 158 | sqlite3_free(err_msg); 159 | return false; 160 | } 161 | //free memory 162 | sqlite3_finalize(stmt); 163 | return true; 164 | } 165 | 166 | bool PManager::replace_category(Category *old_category, Category *new_category) { 167 | char *err_msg = 0; 168 | char sql[1024]; 169 | if (new_category->getName().length() < 3) return false; 170 | snprintf(sql, 1024, "UPDATE Categories SET name='%s', color='%s' WHERE id=%u;", filterSpecialChars(new_category->getName()).c_str(), filterSpecialChars(new_category->getColor()).c_str(), old_category->getId()); 171 | int rc = sqlite3_exec(this->db, sql, 0, 0, &err_msg); 172 | if (rc != SQLITE_OK ) { 173 | fprintf(stderr, "SQL error: %s\n", err_msg); 174 | sqlite3_free(err_msg); 175 | return false; 176 | } 177 | return true; 178 | } 179 | 180 | list PManager::get_events(Category *c) { 181 | if (c == NULL) return this->get_all_events(); 182 | list result; 183 | sqlite3_stmt *res; 184 | int rc = sqlite3_prepare_v2(this->db, "SELECT * FROM Events WHERE category = ?;", -1, &res, NULL); 185 | if (rc != SQLITE_OK) { 186 | fprintf(stderr, "Failed to fetch data: %s\n", sqlite3_errmsg(db)); 187 | return result; 188 | } 189 | sqlite3_bind_int64(res, 1, (long)c->getId()); 190 | while ((rc = sqlite3_step(res)) == SQLITE_ROW) { 191 | unsigned long id = (unsigned long)sqlite3_column_int(res, 0); 192 | string name((const char*)sqlite3_column_text(res, 1)); 193 | string description((const char*)sqlite3_column_text(res, 2)); 194 | string place((const char*)sqlite3_column_text(res, 3)); 195 | Category *category = this->get_category((unsigned long)sqlite3_column_int64(res, 4)); 196 | if (category == NULL) { 197 | fprintf(stderr, "Error: Received NULL category\n"); 198 | continue; 199 | } 200 | time_t start = (unsigned long)sqlite3_column_int64(res, 5); 201 | time_t end = (unsigned long)sqlite3_column_int64(res, 6); 202 | Event *e = new Event(id, name, description, place, category, start, end); 203 | 204 | result.push_front(e); 205 | } 206 | sqlite3_finalize(res); 207 | return result; 208 | } 209 | 210 | list PManager::get_events_of_month(int month, int year) { 211 | list result; 212 | 213 | /* A large number of countries use daylight saving time (DST) (called also summer time). 214 | * We need to manage DST. Most of the countries start to use DST between 1 March and 1 April, 215 | * but the dates where the DST ends are different. For example in Europe DST ends before 1 November, instead in U.S.A. 216 | * DST ends after the 1 November. We must calculate for the current user if DST ends before or after 1 November (geolocation dependent). 217 | * The following lines of code use localtime function that takes into account the location of the user. */ 218 | time_t threshold = 26262000; // = 1 November 1970 219 | std::tm *t = localtime(&threshold); 220 | /* From documentation: 221 | * The Daylight Saving Time flag (tm_isdst) is greater than zero if Daylight Saving Time is in effect, 222 | * zero if Daylight Saving Time is not in effect, 223 | * and less than zero if the information is not available. 224 | */ 225 | int s = 0; 226 | if (t->tm_isdst > 0) s = 1; //if tm_isdst is negative, s will have the default value 227 | 228 | std::tm tm; 229 | tm.tm_sec = 0; 230 | tm.tm_min = 0; 231 | tm.tm_hour = 0; 232 | tm.tm_mday = 1; 233 | tm.tm_mon = month - 1; // Assuming month represents Jan with 1 234 | tm.tm_year = year - 1900; // Assuming year is the AD year number 235 | tm.tm_isdst = ((tm.tm_mon > 2) && (tm.tm_mon < 10+s)); // 10 is November 236 | long first_month = static_cast (mktime(&tm)); //first of month 237 | tm.tm_mon = tm.tm_mon + 1; //This could be 12, but isn't a problem 238 | tm.tm_isdst = ((tm.tm_mon > 2) && (tm.tm_mon < 10+s)); 239 | long last_month = static_cast (mktime(&tm)); //last of month 240 | if ((first_month == -1) || (last_month == -1)) { 241 | fprintf(stderr, "Failed to calculate timestamp\n"); 242 | return result; 243 | } 244 | sqlite3_stmt *res; 245 | int rc = sqlite3_prepare_v2(this->db, "SELECT * FROM Events WHERE (start >= ? AND start < ?) OR (end >= ? AND end < ?);", -1, &res, NULL); 246 | if (rc != SQLITE_OK) { 247 | fprintf(stderr, "Failed to fetch data: %s\n", sqlite3_errmsg(db)); 248 | return result; 249 | } 250 | sqlite3_bind_int64(res, 1, first_month); 251 | sqlite3_bind_int64(res, 2, last_month); 252 | sqlite3_bind_int64(res, 3, first_month); 253 | sqlite3_bind_int64(res, 4, last_month); 254 | while ((rc = sqlite3_step(res)) == SQLITE_ROW) { 255 | unsigned long id = (unsigned long)sqlite3_column_int(res, 0); 256 | string name((const char*)sqlite3_column_text(res, 1)); 257 | string description((const char*)sqlite3_column_text(res, 2)); 258 | string place((const char*)sqlite3_column_text(res, 3)); 259 | Category *category = this->get_category((unsigned long)sqlite3_column_int64(res, 4)); 260 | if (category == NULL) { 261 | fprintf(stderr, "Error: Received NULL category\n"); 262 | continue; 263 | } 264 | time_t start = (unsigned long)sqlite3_column_int64(res, 5); 265 | time_t end = (unsigned long)sqlite3_column_int64(res, 6); 266 | Event *e = new Event(id, name, description, place, category, start, end); 267 | 268 | result.push_front(e); 269 | } 270 | sqlite3_finalize(res); 271 | return result; 272 | } 273 | 274 | bool PManager::remove_db() { 275 | /* Delete the database file, but not the folder */ 276 | return (std::remove(this->db_path.c_str()) == 0); 277 | } 278 | 279 | bool PManager::remove_event(Event *e) { 280 | char *err_msg = 0; 281 | char sql[1024]; 282 | snprintf(sql, 1024, "DELETE FROM Events WHERE id = %u;", e->getId()); 283 | int rc = sqlite3_exec(this->db, sql, 0, 0, &err_msg); 284 | if (rc != SQLITE_OK ) { 285 | fprintf(stderr, "SQL error: %s\n", err_msg); 286 | sqlite3_free(err_msg); 287 | return false; 288 | } 289 | return true; 290 | } 291 | 292 | bool PManager::add_category(Category *c) { 293 | char *err_msg = 0; 294 | char sql[1024]; 295 | if (c->getName().length() < 3) return false; 296 | snprintf(sql, 1024, "INSERT INTO Categories VALUES(%u, '%s', '%s');", c->getId(), filterSpecialChars(c->getName()).c_str(), filterSpecialChars(c->getColor()).c_str()); 297 | int rc = sqlite3_exec(this->db, sql, 0, 0, &err_msg); 298 | if (rc != SQLITE_OK ) { 299 | fprintf(stderr, "SQL error: %s\n", err_msg); 300 | sqlite3_free(err_msg); 301 | return false; 302 | } 303 | return true; 304 | } 305 | 306 | vector PManager::get_categories() { 307 | vector result; 308 | sqlite3_stmt *res; 309 | char sql[1024]; 310 | snprintf(sql, 1024, "SELECT * FROM Categories;"); 311 | int rc = sqlite3_prepare_v2(this->db, sql, -1, &res, 0); 312 | if (rc != SQLITE_OK) { 313 | fprintf(stderr, "Failed to fetch data: %s\n", sqlite3_errmsg(db)); 314 | return result; 315 | } 316 | while ((rc = sqlite3_step(res)) == SQLITE_ROW) { 317 | 318 | Category *c = new Category( sqlite3_column_int(res, 0), 319 | string((const char*)sqlite3_column_text(res, 1)), 320 | string((const char*)sqlite3_column_text(res, 2))); 321 | 322 | result.push_back(c); 323 | } 324 | sqlite3_finalize(res); 325 | return result; 326 | } 327 | 328 | bool PManager::remove_category(Category *c) { 329 | char *err_msg = 0; 330 | char sql[1024]; 331 | snprintf(sql, 1024, "PRAGMA foreign_keys = ON; DELETE FROM Categories WHERE id = %u;", c->getId()); 332 | int rc = sqlite3_exec(this->db, sql, 0, 0, &err_msg); 333 | if (rc != SQLITE_OK ) { 334 | fprintf(stderr, "SQL error: %s\n", err_msg); 335 | sqlite3_free(err_msg); 336 | //Don't close the db 337 | return false; 338 | } 339 | return true; 340 | } 341 | 342 | Category* PManager::get_category(unsigned int id) { 343 | sqlite3_stmt *res; 344 | char sql[1024]; 345 | snprintf(sql, 1024, "SELECT * FROM Categories WHERE id = %u;", id); 346 | int rc = sqlite3_prepare_v2(this->db, sql, -1, &res, 0); 347 | if (rc != SQLITE_OK) { 348 | fprintf(stderr, "Failed to fetch data: %s\n", sqlite3_errmsg(db)); 349 | return NULL; 350 | } 351 | if ((rc = sqlite3_step(res)) == SQLITE_ROW) { 352 | Category *category = new Category( sqlite3_column_int(res, 0), 353 | string((const char*)sqlite3_column_text(res, 1)), 354 | string((const char*)sqlite3_column_text(res, 2))); 355 | sqlite3_finalize(res); 356 | return category; 357 | } else 358 | return NULL; 359 | } 360 | 361 | bool PManager::remove_past_events(time_t timestamp) { 362 | char *err_msg = 0; 363 | char sql[1024]; 364 | snprintf(sql, 1024, "DELETE FROM Events WHERE end <= %ld;", static_cast (timestamp)); 365 | int rc = sqlite3_exec(this->db, sql, 0, 0, &err_msg); 366 | if (rc != SQLITE_OK ) { 367 | fprintf(stderr, "SQL error: %s\n", err_msg); 368 | sqlite3_free(err_msg); 369 | return false; 370 | } 371 | return true; 372 | } 373 | 374 | list PManager::get_all_events() { 375 | list result; 376 | sqlite3_stmt *res; 377 | char sql[1024]; 378 | snprintf(sql, 1024, "SELECT * FROM Events ORDER BY start DESC;"); 379 | int rc = sqlite3_prepare_v2(this->db, sql, -1, &res, 0); 380 | if (rc != SQLITE_OK) { 381 | fprintf(stderr, "Failed to fetch data: %s\n", sqlite3_errmsg(db)); 382 | return result; 383 | } 384 | while ((rc = sqlite3_step(res)) == SQLITE_ROW) { 385 | unsigned long id = (unsigned long)sqlite3_column_int(res, 0); 386 | string name((const char*)sqlite3_column_text(res, 1)); 387 | string description((const char*)sqlite3_column_text(res, 2)); 388 | string place((const char*)sqlite3_column_text(res, 3)); 389 | Category *category = this->get_category((unsigned long)sqlite3_column_int64(res, 4)); 390 | if (category == NULL) { 391 | fprintf(stderr, "Error: Received NULL category\n"); 392 | continue; 393 | } 394 | time_t start = (unsigned long)sqlite3_column_int64(res, 5); 395 | time_t end = (unsigned long)sqlite3_column_int64(res, 6); 396 | Event *e = new Event(id, name, description, place, category, start, end); 397 | 398 | result.push_front(e); 399 | } 400 | sqlite3_finalize(res); 401 | return result; 402 | } 403 | 404 | int PManager::save_db(string path) { 405 | if (path.length() < 5) return 0; 406 | if (path.substr(path.length()-4, 4) != ".kal") path += ".kal"; 407 | int counter = 0; 408 | ofstream file; 409 | file.open(path); 410 | //TODO: An event with a description too long can exceed the buffer, use sqlite3_prepare_v2 411 | char sql[4096]; 412 | vector categories_list = get_categories(); 413 | for (Category *category : categories_list) { 414 | snprintf(sql, 4096, "INSERT INTO Categories VALUES(%u, '%s', '%s');", category->getId(), category->getName().c_str(), category->getColor().c_str()); 415 | file << sql << endl; 416 | delete category; 417 | counter++; 418 | } 419 | list events_list = get_all_events(); 420 | for (Event *event : events_list) { 421 | string description_single_line = event->getDescription(); 422 | size_t start_pos = 0; 423 | while((start_pos = description_single_line.find("\n", start_pos)) != std::string::npos) { 424 | description_single_line.replace(start_pos, 1, "\\n"); 425 | start_pos += 2; 426 | } 427 | snprintf(sql, 1024, "INSERT INTO Events VALUES(%u, '%s', '%s', '%s', %u, %ld, %ld);", event->getId(), event->getName().c_str(), description_single_line.c_str(), event->getPlace().c_str(), event->getCategory()->getId(), event->getStart(), event->getEnd()); 428 | file << sql << endl; 429 | delete event; 430 | counter++; 431 | } 432 | file.close(); 433 | return counter; 434 | } 435 | 436 | /* it's easy load a crafted file with malicious queries (e.g. "DELETE ... ") 437 | * we assume the user knows the source and the content of the file. 438 | */ 439 | int PManager::load_db(string path) { 440 | if ((path.length() < 5) || (path.substr(path.length()-4, 4) != ".kal")) return 0; 441 | ifstream file; 442 | string line; 443 | int rc; 444 | int counter = 0; 445 | char *err_msg = 0; 446 | file.open(path); 447 | while ( getline (file,line) && (line.substr(0, 6) == "INSERT") ) { 448 | //Transform line into a multiline (replacing \n) 449 | size_t start_pos = 0; 450 | while((start_pos = line.find("\\n", start_pos)) != std::string::npos) { 451 | line.replace(start_pos, 2, "\n"); 452 | start_pos += 1; 453 | } 454 | rc = sqlite3_exec(this->db, line.c_str(), 0, 0, &err_msg); 455 | if (rc != SQLITE_OK ) { 456 | fprintf(stderr, "SQL error: %s\n", err_msg); 457 | sqlite3_free(err_msg); 458 | } else counter++; 459 | } 460 | file.close(); 461 | return counter; 462 | } 463 | 464 | int PManager::import_db_iCal_format(string path, Category *category) { 465 | auto category_id = category->getId(); 466 | if ((path.length() < 5) || (path.substr(path.length()-4, 4) != ".ics")) return 0; 467 | ifstream file; 468 | string line; 469 | string pattern; 470 | string summary; 471 | string location; 472 | string description; 473 | bool found_description = false; 474 | int counter = 0; 475 | struct tm start; 476 | start.tm_sec = start.tm_min = start.tm_hour = start.tm_wday = start.tm_yday = start.tm_year = start.tm_mday = start.tm_mon = 0; 477 | struct tm end; 478 | end.tm_sec = end.tm_min = end.tm_hour = end.tm_wday = end.tm_yday = end.tm_year = end.tm_mday = end.tm_mon = 0; 479 | /* Look at the explanation in get_events_of_month */ 480 | time_t threshold = 26262000; // = 1 November 1970 481 | std::tm *t = localtime(&threshold); 482 | int s = 0; 483 | if (t->tm_isdst > 0) s = 1; 484 | 485 | file.open(path); 486 | while ( getline (file,line) ) { 487 | pattern = "DTSTART;VALUE=DATE:"; 488 | if (line.find(pattern) == 0) { //if line starts with the pattern 489 | found_description = false; 490 | string date = line.substr(pattern.length(),line.length()-pattern.length()); 491 | start.tm_year = stoi(date.substr(0,4)) - 1900; 492 | start.tm_mon = stoi(date.substr(4,2)) - 1; 493 | start.tm_mday = stoi(date.substr(6,2)); 494 | start.tm_hour = 8; 495 | start.tm_isdst = ((start.tm_mon > 2) && (start.tm_mon < 10+s)); 496 | continue; 497 | } 498 | pattern = "DTEND;VALUE=DATE:"; 499 | if (line.find(pattern) == 0) { 500 | found_description = false; 501 | string date = line.substr(pattern.length(),line.length()-pattern.length()); 502 | end.tm_year = stoi(date.substr(0,4)) - 1900; 503 | end.tm_mon = stoi(date.substr(4,2)) - 1; 504 | end.tm_mday = stoi(date.substr(6,2)) - 1; /* -1 is to get the day before, mktime will normalize it */ 505 | end.tm_hour = 22; 506 | end.tm_isdst = ((end.tm_mon > 2) && (end.tm_mon < 10+s)); 507 | continue; 508 | } 509 | pattern = "SUMMARY:"; 510 | if (line.find(pattern) == 0) { 511 | found_description = false; 512 | summary = line.substr(pattern.length(),line.length()-pattern.length()); 513 | continue; 514 | } 515 | pattern = "LOCATION:"; 516 | if (line.find(pattern) == 0) { 517 | found_description = false; 518 | location = line.substr(pattern.length(),line.length()-pattern.length()); 519 | if (location.length() < 3) location = ""; 520 | continue; 521 | } 522 | pattern = "DESCRIPTION:"; 523 | if (line.find(pattern) == 0) { 524 | found_description = true; 525 | description = line.substr(pattern.length(),line.length()-pattern.length()); 526 | if (description.length() < 3) description = ""; 527 | continue; 528 | } 529 | pattern = "END:VEVENT"; 530 | if (line.find(pattern) == 0) { 531 | found_description = false; 532 | if (this->add_event(new Event(0,summary,description,location,this->get_category(category_id),mktime(&start),mktime(&end)))) 533 | counter++; 534 | else 535 | printf("Error: %s not imported\n", summary.c_str()); 536 | /* Reset optional variables to import the next event without old values */ 537 | location = ""; 538 | description = ""; 539 | continue; 540 | } 541 | if (found_description) { /* Multi-line description */ 542 | description = description + "\n" + line; 543 | } 544 | } 545 | file.close(); 546 | return counter; 547 | } 548 | 549 | int PManager::export_db_iCal_format(list events, string path) { 550 | if (path.length() < 5) return 0; 551 | if (path.substr(path.length()-4, 4) != ".ics") path = path + ".ics"; 552 | char buff[9]; 553 | ofstream file; 554 | file.open(path); 555 | file << "BEGIN:VCALENDAR" << endl; 556 | file << "CALSCALE:GREGORIAN" << endl; 557 | for (Event *event : events) { 558 | file << "BEGIN:VEVENT" << endl; 559 | time_t tmp = event->getStart(); 560 | strftime(buff, sizeof(buff),"%Y%m%d",localtime((const time_t*)&tmp)); 561 | file << "DTSTART;VALUE=DATE:" << buff << endl; 562 | tmp = event->getEnd(); 563 | struct tm *end = localtime((const time_t*)&tmp); 564 | end->tm_mday += 1; 565 | tmp = mktime(end); 566 | strftime(buff, sizeof(buff),"%Y%m%d",localtime((const time_t*)&tmp)); 567 | file << "DTEND;VALUE=DATE:" << buff << endl; 568 | file << "UID:" << to_string(event->getId()) << endl; 569 | file << "DESCRIPTION:" << event->getDescription() << endl; 570 | file << "LOCATION:" << event->getPlace() << endl; 571 | file << "SUMMARY:" << event->getName() << endl; 572 | file << "END:VEVENT" << endl; 573 | } 574 | file << "END:VCALENDAR" << endl; 575 | file.close(); 576 | return events.size(); 577 | } 578 | -------------------------------------------------------------------------------- /src/persistence/pmanager.h: -------------------------------------------------------------------------------- 1 | #ifndef PMANAGER_H 2 | #define PMANAGER_H 3 | 4 | #include /* Version: 3.9.2 */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "../model/event.h" 16 | #include "../model/category.h" 17 | 18 | #define FOLDER_NAME "kalendar" 19 | #define DEFAULT_DATABASE_NAME "default.sql" 20 | 21 | using namespace std; 22 | static string db_name = DEFAULT_DATABASE_NAME; 23 | 24 | class PManager 25 | { 26 | private: 27 | sqlite3 *db = NULL; 28 | string db_path; 29 | string db_folder; 30 | string filterSpecialChars(string str); 31 | 32 | public: 33 | PManager(string database = DEFAULT_DATABASE_NAME); 34 | ~PManager(); 35 | void init_db(string db_name); 36 | void set_db(string database); 37 | string get_db_name(); 38 | string get_db_folder(); 39 | vector get_db_list(); 40 | bool add_event (Event *e, Event *child = NULL); 41 | bool replace_event (Event *old_event, Event *new_event); //return true also if old_event doesn't exist 42 | bool remove_event(Event *e); 43 | bool remove_db(); 44 | list get_events_of_month(int month, int year); 45 | list get_events(Category *c); 46 | list get_all_events(); 47 | bool add_category (Category *c); 48 | /* Note: the id will not be changed (to avoid to change the events with a reference to the category */ 49 | bool replace_category(Category *old_category, Category *new_category); 50 | bool remove_category(Category *c); 51 | vector get_categories(); 52 | Category *get_category(unsigned int id); 53 | bool remove_past_events(time_t timestamp); 54 | int save_db(string path); 55 | int export_db_iCal_format(list events, string path); 56 | int load_db(string path); 57 | int import_db_iCal_format(string path,Category *category); 58 | }; 59 | 60 | #endif // PMANAGER_H 61 | -------------------------------------------------------------------------------- /src/persistence/securepmanager.cpp: -------------------------------------------------------------------------------- 1 | #include "securepmanager.h" 2 | #include 3 | 4 | SecurePManager::SecurePManager(string database) : PManager(database) 5 | { 6 | 7 | } 8 | -------------------------------------------------------------------------------- /src/persistence/securepmanager.h: -------------------------------------------------------------------------------- 1 | #ifndef SECUREPMANAGER_H 2 | #define SECUREPMANAGER_H 3 | 4 | #include "pmanager.h" 5 | 6 | /* Proxy class to encrypt data on disk */ 7 | //TODO: override PManager functions to encrypt/decrypt data. Use AES-256, implement it in util. 8 | class SecurePManager : public PManager 9 | { 10 | public: 11 | SecurePManager(string database = DEFAULT_DATABASE_NAME); 12 | }; 13 | 14 | #endif // SECUREPMANAGER_H 15 | -------------------------------------------------------------------------------- /src/test/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | g++ -std=c++11 main.cpp test.cpp persistence/pmanager_test.cpp util/timeutil_test.cpp ../persistence/pmanager.cpp ../util/timeutil.cpp -o test -lsqlite3 3 | 4 | clean: 5 | rm test 6 | -------------------------------------------------------------------------------- /src/test/main.cpp: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | int main(int argc, char **argv) { 4 | Test test; 5 | test.test_all(); 6 | return 0; 7 | } 8 | -------------------------------------------------------------------------------- /src/test/persistence/pmanager_test.cpp: -------------------------------------------------------------------------------- 1 | #include "pmanager_test.h" 2 | 3 | #include 4 | 5 | PManagerTest::PManagerTest() 6 | { 7 | time_t timestamp = 966038400L; // = 20/12/1999 I think it's better than time(NULL); 8 | string test("test_string"); 9 | string specialchars("'/--\"#@"); 10 | /* Categories */ 11 | this->valid_default_category = new Category(1, string("Default"), string("#1022A0")); 12 | this->valid_category = new Category(0, test, test); 13 | this->valid_category_2 = new Category(100, test, test); 14 | this->noname_category = new Category(0, string(""), test); 15 | this->specialchars_category = new Category(0, specialchars, specialchars); 16 | /* Events */ 17 | this->valid_event = new Event(0, test, test, test, this->valid_default_category, timestamp, timestamp + 100); 18 | this->valid_event_2 = new Event(100, test, test, test, this->valid_default_category, timestamp - 500, timestamp + 1000000); //starts from current month, ends the next month 19 | this->valid_event_3 = new Event(80, test, test, test, this->valid_category_2, timestamp - 500, timestamp + 1000000); 20 | /* Invalid Events */ 21 | this->event_with_null_category = new Event(0, test, test, test, NULL, timestamp, timestamp); 22 | this->event_with_invalid_category = new Event(0, test, test, test, new Category(99, test, test), timestamp, timestamp); 23 | this->noname_event = new Event(1, string(""), test, test, new Category(1, test, test), timestamp, timestamp + 100); 24 | this->invalid_time_event = new Event(1, test, test, test, new Category(1, test, test), timestamp, timestamp - 100); 25 | this->specialchars_event = new Event(1, specialchars, specialchars, specialchars, new Category(1, specialchars, specialchars), timestamp, timestamp + 100); 26 | } 27 | 28 | PManagerTest::~PManagerTest() { 29 | delete this->valid_event; 30 | delete this->valid_event_2; 31 | delete this->valid_event_3; 32 | delete this->noname_event; 33 | delete this->invalid_time_event; 34 | delete this->noname_category; 35 | delete this->valid_category; 36 | delete this->specialchars_category; 37 | delete this->valid_category_2; 38 | delete this->valid_default_category; 39 | delete this->specialchars_event; 40 | delete this->event_with_invalid_category; 41 | delete this->event_with_null_category; 42 | } 43 | 44 | void PManagerTest::test_all() { 45 | test_get_db_name(); 46 | test_remove_db(); 47 | test_init_db(); 48 | test_get_db_list(); 49 | test_add_event(); 50 | test_get_events_of_month(); 51 | test_remove_event(); 52 | test_add_category(); 53 | test_get_categories(); 54 | test_remove_category(); 55 | test_get_category(); 56 | test_edit_event(); 57 | test_get_all_events(); 58 | test_get_events(); 59 | test_remove_past_events(); 60 | test_edit_category(); 61 | test_set_db(); 62 | test_load_db(); 63 | test_save_db(); 64 | test_import_db_iCal_format(); 65 | test_export_db_iCal_format(); 66 | } 67 | 68 | void PManagerTest::test_get_db_name() { 69 | Test::print("test_get_db_name "); 70 | PManager pm; 71 | bool ret = (pm.get_db_name() == DEFAULT_DATABASE_NAME); 72 | PManager pm2("test.sql"); 73 | ASSERT (ret && (pm2.get_db_name() == "test.sql")) 74 | pm.remove_db(); 75 | pm2.remove_db(); 76 | } 77 | 78 | void PManagerTest::test_remove_db() { 79 | Test::print("test_remove_db "); 80 | PManager pm; 81 | bool ret = pm.remove_db(); 82 | if (pm.get_db_name() != DEFAULT_DATABASE_NAME) { 83 | ret = ret && !std::experimental::filesystem::exists(pm.get_db_folder() + pm.get_db_name()); 84 | } else { 85 | ret = ret && std::experimental::filesystem::exists(pm.get_db_folder() + pm.get_db_name()); 86 | } 87 | ASSERT (ret) 88 | } 89 | 90 | void PManagerTest::test_init_db() { 91 | Test::print("test_init_db "); 92 | PManager pm; 93 | pm.init_db("test.sql"); 94 | ASSERT (std::experimental::filesystem::exists(pm.get_db_folder() + "test.sql")) 95 | pm.remove_db(); 96 | } 97 | 98 | void PManagerTest::test_get_db_list() { 99 | Test::print("test_get_db_list "); 100 | string names[] = {"test1.sql", "test2.sql"}; 101 | PManager pm; 102 | pm.remove_db(); 103 | pm.init_db(names[0]); 104 | bool ret = (std::experimental::filesystem::exists(pm.get_db_folder() + names[0])); 105 | pm.remove_db(); 106 | pm.init_db(names[1]); 107 | ASSERT (ret && std::experimental::filesystem::exists(pm.get_db_folder() + names[1])) 108 | pm.remove_db(); 109 | } 110 | 111 | void PManagerTest::test_add_event() { 112 | Test::print("test_add_event "); 113 | PManager pm; 114 | ASSERT ((!(pm.add_event(this->noname_event))) && 115 | (!(pm.add_event(this->invalid_time_event))) && 116 | (!(pm.add_event(this->event_with_invalid_category))) && 117 | (!(pm.add_event(this->event_with_null_category))) && 118 | (pm.add_event(this->specialchars_event)) && 119 | (pm.add_event(this->valid_event))) 120 | pm.remove_db(); 121 | } 122 | 123 | void PManagerTest::test_get_events_of_month() { 124 | Test::print("test_get_events_of_month "); 125 | bool ret = false, ret2 = false; 126 | PManager pm; 127 | pm.add_event(this->valid_event); 128 | time_t timestamp = this->valid_event->getStart(); 129 | struct tm *current_time = localtime(×tamp); 130 | list events = pm.get_events_of_month(current_time->tm_mon + 1, current_time->tm_year + 1900); // tm_mon is from 0 to 11, we need to have 1 - 12 131 | if (!(events.empty())) { 132 | list::iterator it = events.begin(); 133 | ret = this->valid_event->equals(**it); // *it has type Event* 134 | delete *it; 135 | } 136 | pm.add_event(this->valid_event_2); 137 | timestamp = this->valid_event_2->getEnd(); 138 | current_time = localtime(×tamp); 139 | events = pm.get_events_of_month(current_time->tm_mon + 1, current_time->tm_year + 1900); 140 | if (!(events.empty())) { 141 | list::iterator it = events.begin(); 142 | ret2 = this->valid_event_2->equals(**it); // *it has type Event* 143 | delete *it; 144 | } 145 | ASSERT (ret && ret2) 146 | pm.remove_db(); 147 | } 148 | 149 | void PManagerTest::test_remove_event() { 150 | Test::print("test_remove_event "); 151 | bool ret; 152 | PManager pm; 153 | pm.add_event(this->valid_event); 154 | pm.add_event(this->valid_event_2); 155 | ret = pm.remove_event(this->valid_event); 156 | time_t timestamp = this->valid_event->getStart(); 157 | struct tm *current_time = localtime(×tamp); 158 | list events = pm.get_events_of_month(current_time->tm_mon + 1, current_time->tm_year + 1900); 159 | if (events.size() == 1) { 160 | list::iterator it = events.begin(); 161 | ret = ret && this->valid_event_2->equals(**it); // *it has type Event* 162 | delete *it; 163 | } else ret = false; 164 | ASSERT (ret) 165 | pm.remove_db(); 166 | } 167 | 168 | void PManagerTest::test_add_category() { 169 | Test::print("test_add_category "); 170 | PManager pm; 171 | ASSERT ((!(pm.add_category(this->noname_category))) && (pm.add_category(this->valid_category))) 172 | pm.remove_db(); 173 | } 174 | 175 | void PManagerTest::test_get_categories() { 176 | Test::print("test_get_categories "); 177 | bool ret = false; 178 | PManager pm; 179 | pm.add_category(this->valid_category); 180 | vector categories = pm.get_categories(); 181 | if (!(categories.empty())) { 182 | vector::iterator it = categories.begin(); 183 | ret = (this->valid_category->equals(**it) || this->valid_default_category->equals(**it)); // *it has type Category* 184 | delete *it; 185 | it++; 186 | ret = ret && (this->valid_category->equals(**it) || this->valid_default_category->equals(**it)); // *it has type Category* 187 | delete *it; 188 | } 189 | ASSERT (ret) 190 | pm.remove_db(); 191 | } 192 | 193 | void PManagerTest::test_remove_category() { 194 | Test::print("test_remove_category "); 195 | bool ret; 196 | PManager pm; 197 | pm.add_category(this->valid_category); 198 | pm.add_category(this->valid_category_2); 199 | ret = pm.remove_category(this->valid_category); 200 | ret = ret && pm.remove_category(this->valid_default_category); //Delete the default category 201 | vector categories = pm.get_categories(); 202 | if (categories.size() == 1) { 203 | vector::iterator it = categories.begin(); 204 | ret = ret && this->valid_category_2->equals(**it); // *it has type Category* 205 | delete *it; 206 | } else ret = false; 207 | ret = ret && pm.remove_category(this->valid_category_2); 208 | ret = ret && (pm.get_categories().size() == 0); 209 | pm.add_category(this->valid_default_category); 210 | pm.add_event(this->valid_event); 211 | ret = ret && (!pm.remove_category(this->valid_default_category)); //try to delete the default category, but it's referenced by valid_event, so the function should fails 212 | categories = pm.get_categories(); 213 | if (categories.size() == 1) { 214 | vector::iterator it = categories.begin(); 215 | ret = ret && this->valid_default_category->equals(**it); // *it has type Category* 216 | delete *it; 217 | } else ret = false; 218 | ASSERT (ret) 219 | pm.remove_db(); 220 | } 221 | 222 | void PManagerTest::test_get_category() { 223 | Test::print("test_get_category "); 224 | PManager pm; 225 | pm.add_category(this->valid_category); 226 | ASSERT ((pm.get_category(this->valid_category->getId()))->equals(*this->valid_category)) 227 | pm.remove_db(); 228 | } 229 | 230 | void PManagerTest::test_edit_event() { 231 | Test::print("test_edit_event "); 232 | PManager pm; 233 | pm.add_event(this->valid_event); 234 | ASSERT (pm.replace_event(this->valid_event, this->valid_event_2) && 235 | (!pm.replace_event(this->valid_event_2, this->noname_event))) 236 | pm.remove_db(); 237 | } 238 | 239 | void PManagerTest::test_get_all_events() { 240 | Test::print("test_get_all_events "); 241 | bool ret = false, ret2 = false; 242 | PManager pm; 243 | pm.add_event(this->valid_event); 244 | pm.add_event(this->valid_event_2); 245 | list events = pm.get_all_events(); 246 | if (!(events.empty())) { 247 | list::iterator it = events.begin(); 248 | ret = this->valid_event_2->equals(**it); // *it has type Event* 249 | delete *it; 250 | it++; 251 | ret2 = this->valid_event->equals(**it); 252 | delete *it; 253 | } 254 | ASSERT (ret && ret2) 255 | pm.remove_db(); 256 | } 257 | 258 | void PManagerTest::test_get_events() { 259 | Test::print("test_get_events "); 260 | bool ret = false; 261 | PManager pm; 262 | pm.add_event(this->valid_event); 263 | pm.add_category(this->valid_category_2); 264 | pm.add_event(this->valid_event_3); 265 | list events_cat1 = pm.get_events(this->valid_default_category); 266 | ret = (events_cat1.size() == 1); 267 | list::iterator it1 = events_cat1.begin(); 268 | ret = ret && this->valid_event->equals(**it1); 269 | delete *it1; 270 | list events_cat2 = pm.get_events(this->valid_category_2); 271 | ret = ret && (events_cat2.size() == 1); 272 | list::iterator it2 = events_cat2.begin(); 273 | ret = ret && this->valid_event_3->equals(**it2); 274 | delete *it2; 275 | list events = pm.get_events(NULL); 276 | ret = ret && (events.size() == 2); 277 | ASSERT (ret) 278 | pm.remove_db(); 279 | } 280 | 281 | void PManagerTest::test_remove_past_events() { 282 | Test::print("test_remove_past_events "); 283 | bool ret; 284 | PManager pm; 285 | pm.add_event(this->valid_event); 286 | pm.add_event(this->valid_event_2); 287 | ret = pm.remove_past_events(this->valid_event_2->getEnd()-1); 288 | list events = pm.get_all_events(); 289 | if (events.size() == 1) { 290 | list::iterator it = events.begin(); 291 | ret = ret && this->valid_event_2->equals(**it); // *it has type Event* 292 | delete *it; 293 | } else ret = false; 294 | ASSERT (ret) 295 | pm.remove_db(); 296 | } 297 | 298 | void PManagerTest::test_edit_category() { 299 | Test::print("test_edit_category "); 300 | PManager pm; 301 | pm.add_category(this->valid_category); 302 | ASSERT (!pm.replace_category(this->valid_category, this->noname_category) && 303 | (pm.replace_category(this->valid_category, this->valid_category_2))) 304 | pm.remove_db(); 305 | } 306 | 307 | void PManagerTest::test_set_db() { 308 | Test::print("test_set_db "); 309 | PManager pm; 310 | pm.init_db("test.sql"); 311 | pm.set_db(DEFAULT_DATABASE_NAME); 312 | bool ret (pm.get_db_name() == DEFAULT_DATABASE_NAME); 313 | pm.add_category(this->valid_category); 314 | pm.add_event(this->valid_event); 315 | pm.set_db("test.sql"); 316 | ret = ret && (pm.get_db_name() == "test.sql"); 317 | pm.add_category(this->valid_category_2); 318 | pm.add_event(this->valid_event_2); 319 | pm.add_event(this->valid_event_3); 320 | pm.set_db(DEFAULT_DATABASE_NAME); 321 | ret = ret && (pm.get_db_name() == DEFAULT_DATABASE_NAME); 322 | ret = ret && ((pm.get_all_events().size() == 1) && 323 | (pm.get_all_events().front()->equals(*this->valid_event))); 324 | pm.remove_db(); 325 | pm.set_db("test.sql"); 326 | ret = ret && (pm.get_db_name() == "test.sql"); 327 | ASSERT (ret && (pm.get_all_events().size() == 2)) 328 | pm.remove_db(); 329 | } 330 | 331 | void PManagerTest::test_load_db() { 332 | Test::print("test_load_db "); 333 | bool ret; 334 | PManager pm; 335 | ofstream file; 336 | file.open("testdb.kal"); 337 | file << "INSERT INTO Categories VALUES(" << this->valid_category->getId() << ",'" << this->valid_category->getName() << "','" << this->valid_category->getColor() << "');" << endl; 338 | file << "INSERT INTO Events VALUES(" << this->valid_event->getId() << ",'" << this->valid_event->getName() << "','" << this->valid_event->getDescription() << "','" << this->valid_event->getPlace() << "'," << this->valid_event->getCategory()->getId() << "," << this->valid_event->getStart() << "," << this->valid_event->getEnd() << ", NULL);" << endl; 339 | file.close(); 340 | ret = pm.load_db(""); 341 | ret = !ret && pm.load_db("notexist"); 342 | ret = !ret && pm.load_db("testdb.kal"); 343 | Category *category = pm.get_category(this->valid_category->getId()); 344 | ret = ret && category->equals(*this->valid_category); 345 | delete category; 346 | list events = pm.get_all_events(); 347 | if (events.size() == 1) { 348 | list::iterator it = events.begin(); 349 | ret = ret && this->valid_event->equals(**it); // *it has type Event* 350 | delete *it; 351 | } else ret = false; 352 | pm.remove_db(); 353 | remove("testdb.kal"); 354 | ASSERT (ret) 355 | } 356 | 357 | void PManagerTest::test_save_db() { 358 | Test::print("test_save_db "); 359 | bool ret; 360 | PManager pm; 361 | ifstream file; 362 | string line; 363 | char insert_event[1024]; 364 | char insert_category[1024]; 365 | snprintf(insert_event, 1024, "INSERT INTO Events VALUES(%u, '%s', '%s', '%s', %u, %ld, %ld);", this->valid_event->getId(), this->valid_event->getName().c_str(), this->valid_event->getDescription().c_str(), this->valid_event->getPlace().c_str(), this->valid_event->getCategory()->getId(), this->valid_event->getStart(), this->valid_event->getEnd()); 366 | snprintf(insert_category, 1024, "INSERT INTO Categories VALUES(%u, '%s', '%s');", this->valid_default_category->getId(), this->valid_default_category->getName().c_str(), this->valid_default_category->getColor().c_str()); 367 | pm.add_event(this->valid_event); 368 | ret = pm.save_db("testdb"); 369 | file.open("testdb.kal"); 370 | getline (file,line); 371 | ret = ret && ((line == insert_category) || (line == insert_event)); 372 | getline (file,line); 373 | ret = ret && ((line == insert_event) || (line == insert_category)); 374 | getline (file,line); 375 | ret = ret && (line == ""); 376 | file.close(); 377 | pm.remove_db(); 378 | remove("testdb.kal"); 379 | ASSERT (ret) 380 | } 381 | 382 | void PManagerTest::test_import_db_iCal_format() { 383 | Test::print("test_import_db_iCal_format "); 384 | PManager pm; 385 | ofstream file; 386 | bool ret; 387 | ret = pm.import_db_iCal_format("",this->valid_default_category); 388 | ret = !ret && !pm.import_db_iCal_format("notexists",this->valid_default_category); 389 | file.open("temp.ics"); 390 | file << "BEGIN:VEVENT" << endl << "UID:0" << endl << "DTSTART;VALUE=DATE:20161231" << endl << "DTEND;VALUE=DATE:20170101" << endl << "SUMMARY:test" << endl << "DESCRIPTION:multi\nline\ndescription" << endl << "END:VEVENT" << endl; 391 | file << "BEGIN:VEVENT" << endl << "UID:1" << endl << "DTSTART;VALUE=DATE:20130512" << endl << "DTEND;VALUE=DATE:20130513" << endl << "SUMMARY:test2" << endl << "END:VEVENT" << endl; 392 | file.close(); 393 | ret = ret && pm.import_db_iCal_format("temp.ics",this->valid_default_category); 394 | list events = pm.get_all_events(); 395 | if (events.size() == 2) { 396 | list::iterator it = events.begin(); 397 | ret = ret && ((**it).getName() == "test2"); 398 | ret = ret && ((**it).getCategory()->getId() == this->valid_default_category->getId()); 399 | //TODO: checks if the following test is correct 400 | ret = ret && ((**it).getStart() < (**it).getEnd()); 401 | delete *it; 402 | } else ret = false; 403 | pm.remove_db(); 404 | remove("temp.ics"); 405 | ASSERT (ret) 406 | } 407 | 408 | void PManagerTest::test_export_db_iCal_format() { 409 | Test::print("test_export_db_iCal_format "); 410 | string line; 411 | PManager pm; 412 | pm.add_event(this->valid_event); 413 | ifstream file; 414 | bool ret; 415 | ret = pm.export_db_iCal_format(pm.get_all_events(),""); 416 | ret = !ret && pm.export_db_iCal_format(pm.get_all_events(),"temp.ics"); 417 | file.open("temp.ics"); 418 | int linenumber = 0; 419 | while ( getline (file,line,'\n') ) { 420 | 421 | if (linenumber == 0) { 422 | ret = ret && (line == "BEGIN:VCALENDAR"); 423 | linenumber++; 424 | continue; 425 | } 426 | if (linenumber == 3) { 427 | char buff[9]; 428 | time_t start = this->valid_event->getStart(); 429 | strftime(buff, sizeof(buff),"%Y%m%d",localtime((const time_t*)&start)); 430 | ret = ret && (line == string("DTSTART;VALUE=DATE:") + string(buff)); 431 | linenumber++; 432 | continue; 433 | } 434 | if (linenumber == 5) { 435 | ret = ret && (line == string("UID:") + to_string(this->valid_event->getId())); 436 | linenumber++; 437 | continue; 438 | } 439 | if (linenumber == 8) { 440 | ret = ret && (line == string("SUMMARY:") + this->valid_event->getName()); 441 | linenumber++; 442 | continue; 443 | } 444 | linenumber++; 445 | } 446 | file.close(); 447 | pm.remove_db(); 448 | remove("temp.ics"); 449 | ASSERT (ret) 450 | } 451 | -------------------------------------------------------------------------------- /src/test/persistence/pmanager_test.h: -------------------------------------------------------------------------------- 1 | #ifndef PMANAGERTEST_H 2 | #define PMANAGERTEST_H 3 | 4 | #include 5 | 6 | #include "../test.h" 7 | #include "../../persistence/pmanager.h" 8 | 9 | class PManagerTest 10 | { 11 | private: 12 | Event *valid_event; 13 | Event *valid_event_2; 14 | Event *valid_event_3; 15 | Event *noname_event; 16 | Event *specialchars_event; 17 | Event *invalid_time_event; 18 | Event *event_with_invalid_category; 19 | Event *event_with_null_category; 20 | Category *valid_category; 21 | Category *valid_category_2; 22 | Category *valid_default_category; 23 | Category *noname_category; 24 | Category *specialchars_category; 25 | 26 | public: 27 | PManagerTest(); 28 | ~PManagerTest(); 29 | void test_all(); 30 | void test_init_db(); 31 | void test_add_event(); 32 | void test_remove_event(); 33 | void test_add_category(); 34 | void test_get_events_of_month(); 35 | void test_remove_category(); 36 | void test_get_categories(); 37 | void test_remove_db(); 38 | void test_get_category(); 39 | void test_edit_event(); 40 | void test_get_events(); 41 | void test_get_all_events(); 42 | void test_remove_past_events(); 43 | void test_edit_category(); 44 | void test_load_db(); 45 | void test_save_db(); 46 | void test_import_db_iCal_format(); 47 | void test_export_db_iCal_format(); 48 | void test_get_db_name(); 49 | void test_set_db(); 50 | void test_get_db_list(); 51 | }; 52 | 53 | #endif // PMANAGERTEST_H 54 | -------------------------------------------------------------------------------- /src/test/test.cpp: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | Test::Test() 4 | { 5 | 6 | } 7 | 8 | void Test::test_all() { 9 | test_persistence(); 10 | test_util(); 11 | } 12 | 13 | void Test::test_persistence() { 14 | print("\nStarting persistence tests\n"); 15 | PManagerTest pmt; 16 | pmt.test_all(); 17 | } 18 | void Test::test_util() { 19 | print("\nStarting util tests\n"); 20 | DateUtilTest dut; 21 | dut.test_all(); 22 | printf("date util disabilitato\n"); 23 | EventUtilTest eut; 24 | eut.test_all(); 25 | } 26 | -------------------------------------------------------------------------------- /src/test/test.h: -------------------------------------------------------------------------------- 1 | #ifndef TEST_H 2 | #define TEST_H 3 | 4 | #include 5 | #include "persistence/pmanager_test.h" 6 | #include "util/dateutil_test.h" 7 | #include "util/eventutil_test.h" 8 | 9 | #define ASSERT(X) if (X) Test::print_green("passed\n"); else { Test::print_red("fail\n"); exit(1); } 10 | 11 | using namespace std; 12 | 13 | /* Unit testing class */ 14 | 15 | class Test 16 | { 17 | public: 18 | Test(); 19 | void test_all(); 20 | void test_persistence(); 21 | void test_util(); 22 | /* fflush needed to see the output on the Application Output of QT Creator */ 23 | static void print_green(string s) { printf("\033[32m%s\033[39m", s.c_str()); fflush(stdout); } 24 | static void print_red(string s) { printf("\033[31m%s\033[39m", s.c_str()); fflush(stdout); } 25 | static void print(string s) { printf("%s", s.c_str()); fflush(stdout); } 26 | }; 27 | 28 | #endif // TEST_H 29 | -------------------------------------------------------------------------------- /src/test/util/dateutil_test.cpp: -------------------------------------------------------------------------------- 1 | #include "dateutil_test.h" 2 | 3 | #include 4 | 5 | DateUtilTest::DateUtilTest() 6 | { 7 | this->mar_10_2016 = new Date(10, 4, 3, 2016); 8 | this->may_1_2102 = new Date(1, 1, 5, 2102); 9 | this->sep_28_1927 = new Date(28, 3, 9, 1927); 10 | this->jan_1_2015 = new Date(1, 4, 1, 2015); 11 | this->dec_31_2014 = new Date(31, 3, 12, 2014); 12 | this->feb_29_2016 = new Date(29, 1, 2, 2016); 13 | this->mar_1_2015 = new Date(1, 7, 3, 2015); 14 | this->mar_1_2016 = new Date(1, 2, 3, 2016); 15 | this->mar_1_2017 = new Date(1, 3, 3, 2017); 16 | this->mar_1_2018 = new Date(1, 4, 3, 2018); 17 | this->sep_1_1927 = new Date(1, 4, 9, 1927); 18 | this->jan_12_2015 = new Date(12, 1, 1, 2015); 19 | this->jan_31_2015 = new Date(31, 6, 1, 2015); 20 | this->feb_21_2016 = new Date(21, 7, 2, 2016); 21 | this->feb_1_2016 = new Date(1, 1, 2, 2016); 22 | this->dec_14_2014 = new Date(14, 7, 12, 2014); 23 | this->aug_20_2000 = new Date(20, 7, 8, 2000); 24 | this->aug_21_2000 = new Date(21, 1, 8, 2000); 25 | } 26 | 27 | DateUtilTest::~DateUtilTest() 28 | { 29 | delete this->mar_10_2016; 30 | delete this->may_1_2102; 31 | delete this->sep_28_1927; 32 | delete this->jan_1_2015; 33 | delete this->dec_31_2014; 34 | delete this->feb_29_2016; 35 | delete this->mar_1_2015; 36 | delete this->mar_1_2016; 37 | delete this->mar_1_2017; 38 | delete this->mar_1_2018; 39 | delete this->sep_1_1927; 40 | delete this->jan_12_2015; 41 | delete this->feb_21_2016; 42 | delete this->feb_1_2016; 43 | delete this->dec_14_2014; 44 | delete this->aug_20_2000; 45 | delete this->aug_21_2000; 46 | } 47 | 48 | void DateUtilTest::test_all() { 49 | test_get_days_in_month(); 50 | test_literal2numeric_day_of_week(); 51 | test_numeric2literal_day_of_week(); 52 | test_get_literal_month(); 53 | test_date_from_timestamp(); 54 | test_get_first_day_of_month(); 55 | test_get_last_day_of_month(); 56 | test_increase_month(); 57 | test_decrease_month(); 58 | test_increase_day(); 59 | test_decrease_day(); 60 | test_increase_year(); 61 | test_decrease_year(); 62 | test_is_leap(); 63 | } 64 | 65 | void DateUtilTest::test_get_days_in_month() { 66 | Test::print("test_get_days_in_month "); 67 | ASSERT ((DateUtil::get_days_in_month(this->feb_29_2016->getMonth(), this->feb_29_2016->getYear()) == 29) && 68 | (DateUtil::get_days_in_month(this->jan_1_2015->getMonth(), this->jan_1_2015->getYear()) == 31) && //January 69 | (DateUtil::get_days_in_month(this->sep_28_1927->getMonth(), this->sep_28_1927->getYear()) == 30)) 70 | } 71 | 72 | void DateUtilTest::test_literal2numeric_day_of_week() { 73 | Test::print("test_literal2numeric_day_of_week "); 74 | ASSERT ((DateUtil::literal2numeric_day_of_week(string("Monday")) == 1) && 75 | (DateUtil::literal2numeric_day_of_week(string("Sunday")) == 7) && 76 | (DateUtil::literal2numeric_day_of_week(string("Thursday")) == 4) && 77 | (DateUtil::literal2numeric_day_of_week(string("")) == -1)) 78 | } 79 | 80 | void DateUtilTest::test_numeric2literal_day_of_week() { 81 | Test::print("test_numeric2literal_day_of_week "); 82 | ASSERT ((DateUtil::numeric2literal_day_of_week(1) == string("Monday")) && 83 | (DateUtil::numeric2literal_day_of_week(7) == string("Sunday")) && 84 | (DateUtil::numeric2literal_day_of_week(2) == string("Tuesday")) && 85 | (DateUtil::numeric2literal_day_of_week(0) == string("")) && 86 | (DateUtil::numeric2literal_day_of_week(8) == string(""))) 87 | } 88 | 89 | void DateUtilTest::test_get_literal_month() { 90 | Test::print("test_timeutil_get_literal_month "); 91 | ASSERT ((DateUtil::get_literal_month(1) == string("January")) && 92 | (DateUtil::get_literal_month(12) == string("December")) && 93 | (DateUtil::get_literal_month(8) == string("August")) && 94 | (DateUtil::get_literal_month(0) == string("")) && 95 | (DateUtil::get_literal_month(13) == string(""))) 96 | } 97 | 98 | void DateUtilTest::test_get_first_day_of_month() { 99 | Test::print("test_get_first_day_of_month "); 100 | ASSERT ((DateUtil::get_first_day_of_month(*this->jan_1_2015) == *this->jan_1_2015) && 101 | (DateUtil::get_first_day_of_month(*this->mar_10_2016) == *this->mar_1_2016) && 102 | (DateUtil::get_first_day_of_month(*this->sep_28_1927) == *this->sep_1_1927) && 103 | (DateUtil::get_first_day_of_month(*this->jan_12_2015)) == *this->jan_1_2015 && 104 | (DateUtil::get_first_day_of_month(*this->feb_21_2016)) == *this->feb_1_2016) 105 | } 106 | 107 | void DateUtilTest::test_date_from_timestamp() { 108 | Test::print("test_date_from_timestamp "); 109 | ASSERT ((DateUtil::date_from_timestamp(1420070400)) == *this->jan_1_2015 && 110 | (DateUtil::date_from_timestamp(1419984000)) == *this->dec_31_2014 && 111 | (DateUtil::date_from_timestamp(1456012800)) == *this->feb_21_2016 && 112 | (DateUtil::date_from_timestamp(4175884800)) == *this->may_1_2102 && 113 | /* remember: the timestamp will be converted into an unsigned long */ 114 | (DateUtil::date_from_timestamp(-1333670400)) == *this->sep_28_1927) 115 | } 116 | 117 | void DateUtilTest::test_get_last_day_of_month() { 118 | Test::print("test_get_last_day_of_month "); 119 | ASSERT ((DateUtil::get_last_day_of_month(*this->feb_21_2016)) == *this->feb_29_2016 && 120 | (DateUtil::get_last_day_of_month(*this->dec_14_2014)) == *this->dec_31_2014) 121 | } 122 | 123 | void DateUtilTest::test_increase_month() { 124 | Test::print("test_increase_month "); 125 | ASSERT (DateUtil::increase_month(*this->feb_1_2016) == *this->mar_1_2016 && 126 | DateUtil::increase_month(*this->dec_31_2014) == *this->jan_31_2015) 127 | } 128 | 129 | void DateUtilTest::test_decrease_month() { 130 | Test::print("test_decrease_month "); 131 | ASSERT (DateUtil::decrease_month(*this->mar_1_2016) == *this->feb_1_2016 && 132 | DateUtil::decrease_month(*this->jan_31_2015) == *this->dec_31_2014) 133 | } 134 | 135 | void DateUtilTest::test_increase_day() { 136 | Test::print("test_increase_day "); 137 | ASSERT (DateUtil::increase_day(*this->feb_29_2016) == *this->mar_1_2016 && 138 | DateUtil::increase_day(*this->dec_31_2014) == *this->jan_1_2015 && 139 | DateUtil::increase_day(*this->aug_20_2000) == *this->aug_21_2000) 140 | } 141 | 142 | void DateUtilTest::test_decrease_day() { 143 | Test::print("test_decrease_day "); 144 | ASSERT (DateUtil::decrease_day(*this->mar_1_2016) == *this->feb_29_2016 && 145 | DateUtil::decrease_day(*this->jan_1_2015) == *this->dec_31_2014 && 146 | DateUtil::decrease_day(*this->aug_21_2000) == *this->aug_20_2000) 147 | } 148 | 149 | void DateUtilTest::test_increase_year() { 150 | Test::print("test_increase_year "); 151 | ASSERT (DateUtil::increase_year(*this->feb_29_2016) == *this->mar_1_2017 && 152 | DateUtil::increase_year(*this->mar_1_2016) == *this->mar_1_2017 && 153 | DateUtil::increase_year(*this->mar_1_2017) == *this->mar_1_2018 && 154 | DateUtil::increase_year(*this->mar_1_2015) == *this->mar_1_2016) 155 | } 156 | 157 | void DateUtilTest::test_decrease_year() { 158 | Test::print("test_decrease_year "); 159 | ASSERT (DateUtil::decrease_year(*this->mar_1_2017) == *this->mar_1_2016 && 160 | DateUtil::decrease_year(*this->mar_1_2018) == *this->mar_1_2017 && 161 | DateUtil::decrease_year(*this->mar_1_2016) == *this->mar_1_2015) 162 | } 163 | 164 | void DateUtilTest::test_is_leap() { 165 | Test::print("test_is_leap "); 166 | ASSERT (DateUtil::is_leap(this->mar_1_2016->getYear()) && 167 | DateUtil::is_leap(this->aug_20_2000->getYear()) && 168 | !DateUtil::is_leap(this->jan_12_2015->getYear())) 169 | } 170 | -------------------------------------------------------------------------------- /src/test/util/dateutil_test.h: -------------------------------------------------------------------------------- 1 | #ifndef DATEUTILTEST_H 2 | #define DATEUTILTEST_H 3 | 4 | #include "../test.h" 5 | #include "../../util/dateutil.h" 6 | 7 | class DateUtilTest 8 | { 9 | private: 10 | Date *mar_10_2016; 11 | Date *sep_28_1927; 12 | Date *feb_29_2016; 13 | Date *jan_1_2015; 14 | Date *dec_31_2014; 15 | Date *may_1_2102; 16 | Date *mar_1_2015; 17 | Date *mar_1_2016; 18 | Date *mar_1_2017; 19 | Date *mar_1_2018; 20 | Date *sep_1_1927; 21 | Date *jan_12_2015; 22 | Date *feb_21_2016; 23 | Date *feb_1_2016; 24 | Date *dec_14_2014; 25 | Date *jan_31_2015; 26 | Date *aug_20_2000; 27 | Date *aug_21_2000; 28 | 29 | public: 30 | DateUtilTest(); 31 | ~DateUtilTest(); 32 | void test_all(); 33 | void test_get_literal_month(); 34 | void test_numeric2literal_day_of_week(); 35 | void test_literal2numeric_day_of_week(); 36 | void test_get_days_in_month(); 37 | void test_date_from_timestamp(); 38 | void test_get_first_day_of_month(); 39 | void test_get_last_day_of_month(); 40 | void test_increase_month(); 41 | void test_decrease_month(); 42 | void test_increase_day(); 43 | void test_decrease_day(); 44 | void test_increase_year(); 45 | void test_decrease_year(); 46 | void test_is_leap(); 47 | }; 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /src/test/util/eventutil_test.cpp: -------------------------------------------------------------------------------- 1 | #include "eventutil_test.h" 2 | 3 | EventUtilTest::EventUtilTest() 4 | { 5 | time_t timestamp = 966038400L; 6 | string category("Default"); 7 | string title("event title"); 8 | string place("place"); 9 | string description("this is a really complex description. Infact, there are several characters and white-spaces."); 10 | /* Events */ 11 | this->valid_event = new Event(0, title, description, place, new Category(1, category, category), timestamp, timestamp + 100); 12 | this->event_with_null_category = new Event(0, title, description, place, NULL, timestamp, timestamp + 100); 13 | this->str_valid_event = title + "##" + description + "##" + place + "##" + category + "##" + to_string(timestamp) + "##" + to_string(timestamp + 100); 14 | this->str_event_title_only = title; 15 | this->str_event_with_invalid_category = title + "##" + description + "##" + place + "##Doesnt_exists##" + to_string(timestamp) + "##" + to_string(timestamp + 100); 16 | this->str_notitle_event = "##" + description + "##" + place + "##" + category + "##" + to_string(timestamp) + "##" + to_string(timestamp + 100); 17 | } 18 | 19 | EventUtilTest::~EventUtilTest() { 20 | delete this->valid_event; 21 | delete this->event_with_null_category; 22 | } 23 | 24 | void EventUtilTest::test_all() { 25 | test_parseString(); 26 | } 27 | 28 | void EventUtilTest::test_parseString() { 29 | Test::print("test_parseString"); 30 | ASSERT (EventUtil::parseString(this->str_valid_event)->equals(*this->valid_event) && 31 | EventUtil::parseString(this->str_event_with_invalid_category)->equals(*this->event_with_null_category) && 32 | EventUtil::parseString(this->str_event_title_only) == NULL && 33 | EventUtil::parseString(this->str_notitle_event) == NULL) 34 | } 35 | -------------------------------------------------------------------------------- /src/test/util/eventutil_test.h: -------------------------------------------------------------------------------- 1 | #ifndef EVENTUTIL_TEST_H 2 | #define EVENTUTIL_TEST_H 3 | 4 | #include "../test.h" 5 | #include "../../util/eventutil.h" 6 | 7 | class EventUtilTest 8 | { 9 | private: 10 | Event *valid_event; 11 | Event *event_with_null_category; 12 | string str_valid_event; 13 | string str_notitle_event; 14 | string str_event_with_invalid_category; 15 | string str_event_title_only; 16 | 17 | public: 18 | EventUtilTest(); 19 | ~EventUtilTest(); 20 | void test_all(); 21 | void test_parseString(); 22 | }; 23 | 24 | #endif // EVENTUTIL_TEST_H 25 | -------------------------------------------------------------------------------- /src/tools/.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/tools/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | FacebookEventPlugin 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | 15 | org.eclipse.jdt.core.javanature 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/tools/.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled 3 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 4 | org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve 5 | org.eclipse.jdt.core.compiler.compliance=1.8 6 | org.eclipse.jdt.core.compiler.debug.lineNumber=generate 7 | org.eclipse.jdt.core.compiler.debug.localVariable=generate 8 | org.eclipse.jdt.core.compiler.debug.sourceFile=generate 9 | org.eclipse.jdt.core.compiler.problem.assertIdentifier=error 10 | org.eclipse.jdt.core.compiler.problem.enumIdentifier=error 11 | org.eclipse.jdt.core.compiler.source=1.8 12 | -------------------------------------------------------------------------------- /src/tools/fbeventplugin/FBEvent.java: -------------------------------------------------------------------------------- 1 | package fbeventplugin; 2 | 3 | import java.time.ZonedDateTime; 4 | import java.time.ZonedDateTime; 5 | import java.util.Date; 6 | 7 | public class FBEvent { 8 | private String title; 9 | private String description; 10 | private String place; 11 | private ZonedDateTime start; 12 | private ZonedDateTime end; 13 | 14 | @Override 15 | public String toString() { 16 | return "FBEvent [title=" + title + ", description=" + description + ", place=" + place + ", start=" + start.toString() 17 | + ", end=" + end.toString() + "]"; 18 | } 19 | 20 | public FBEvent(String title, String description, String place, ZonedDateTime start, ZonedDateTime end) { 21 | super(); 22 | this.title = title; 23 | this.description = description; 24 | this.place = place; 25 | this.start = start; 26 | this.end = end; 27 | } 28 | 29 | public String getTitle() { 30 | return title; 31 | } 32 | public void setTitle(String title) { 33 | this.title = title; 34 | } 35 | public String getDescription() { 36 | return description; 37 | } 38 | public void setDescription(String description) { 39 | this.description = description; 40 | } 41 | public String getPlace() { 42 | return place; 43 | } 44 | public void setPlace(String place) { 45 | this.place = place; 46 | } 47 | public ZonedDateTime getStart() { 48 | return start; 49 | } 50 | public void setStart(ZonedDateTime start) { 51 | this.start = start; 52 | } 53 | public ZonedDateTime getEnd() { 54 | return end; 55 | } 56 | public void setEnd(ZonedDateTime end) { 57 | this.end = end; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/tools/fbeventplugin/FBParser.java: -------------------------------------------------------------------------------- 1 | package fbeventplugin; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.InputStreamReader; 5 | import java.net.HttpURLConnection; 6 | import java.net.URL; 7 | import java.sql.Timestamp; 8 | import java.text.DateFormat; 9 | import java.text.ParseException; 10 | import java.text.SimpleDateFormat; 11 | import java.time.ZonedDateTime; 12 | import java.util.Date; 13 | 14 | public class FBParser { 15 | 16 | private String filterSpecialChars(String str) { 17 | return str.replaceAll("'", "'") 18 | .replaceAll(""", "\\\"") 19 | .replaceAll("@", "@") 20 | .replaceAll("", "\r\n") 21 | .replaceAll("(?s)<[^>]*>(\\s*<[^>]*>)*", " "); 22 | } 23 | 24 | private FBEvent createEvent(String html) { 25 | String title; 26 | String description; 27 | String place; 28 | ZonedDateTime start = null; 29 | ZonedDateTime end = null; 30 | 31 | int pos = html.indexOf("pageTitle") + 11; 32 | title = filterSpecialChars(html.substring(pos, html.indexOf("", pos))); 38 | } catch (Exception e) { 39 | System.err.println("Exception occurred in date parsing: "+e.getMessage()); 40 | return null; 41 | } 42 | pos = html.indexOf("fcg\">", html.indexOf("event-permalink-location", pos)) + 5; 43 | place = html.substring(pos, html.indexOf("...", ""); 46 | description = filterSpecialChars(html.substring(pos, html.indexOf(" 4 | 5 | static string months[] = {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}; 6 | static string week_days[] = {"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"}; 7 | 8 | DateUtil::DateUtil() 9 | { 10 | 11 | } 12 | 13 | Date DateUtil::get_current_date() { 14 | return date_from_timestamp(static_cast (time(NULL))); 15 | } 16 | 17 | string DateUtil::get_literal_month(int m) { 18 | if ((m < 1) || (m > 12)) 19 | return string(""); 20 | else 21 | return months[m-1]; 22 | } 23 | 24 | string DateUtil::numeric2literal_day_of_week(int d) { 25 | if ((d < 1) || (d > 7)) 26 | return string(""); 27 | else 28 | return week_days[d-1]; 29 | } 30 | 31 | int DateUtil::literal2numeric_day_of_week(const string &d) { 32 | int i = 0; 33 | for (i = 0; i < 7; i++) { 34 | if (d == week_days[i]) 35 | return i+1; 36 | } 37 | return -1; 38 | } 39 | 40 | bool DateUtil::is_leap(int year) { 41 | return ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)); 42 | } 43 | 44 | int DateUtil::get_days_in_month(int month, int year) { 45 | int numberOfDays; 46 | if (month == 4 || month == 6 || month == 9 || month == 11) 47 | numberOfDays = 30; 48 | else if (month == 2) 49 | { 50 | if (is_leap(year)) 51 | numberOfDays = 29; 52 | else 53 | numberOfDays = 28; 54 | } 55 | else 56 | numberOfDays = 31; 57 | return numberOfDays; 58 | } 59 | 60 | /* Assume to receive a valid timestamp */ 61 | Date DateUtil::date_from_timestamp(time_t timestamp) { 62 | const time_t t = timestamp; 63 | struct tm *tm = localtime(&t); 64 | Date date(tm->tm_mday, tm->tm_wday ?: 7, tm->tm_mon + 1, tm->tm_year + 1900); 65 | //free(tm); segfault? 66 | return date; 67 | } 68 | 69 | Date DateUtil::get_first_day_of_month(Date &date) { 70 | int wday = date.getWeekDay() - (date.getMonthDay() % 7) + 1; 71 | //Normalization 72 | if (wday <= 0) wday += 7; 73 | else if (wday > 7) wday -= 7; 74 | return Date(1, wday, date.getMonth(), date.getYear()); 75 | } 76 | 77 | Date DateUtil::get_last_day_of_month(Date &date) { 78 | Date first_day = get_first_day_of_month(date); 79 | int tot_days = get_days_in_month(date.getMonth(), date.getYear()); 80 | int wday = ((tot_days % 7) ?: 7) + first_day.getWeekDay() - 1; 81 | return Date(tot_days, wday, date.getMonth(), date.getYear()); 82 | } 83 | 84 | //Attention: each month has a different number of days (e.g. 31 doesn't exist in february) 85 | //Assume to get a valid time (i.e. no negative numbers, etc.) 86 | Date DateUtil::increase_month(Date date) { 87 | Date last_day_curr_month = get_last_day_of_month(date); 88 | Date first_day_next_month; 89 | if (date.getMonth() < 12) { 90 | first_day_next_month = Date(1, (last_day_curr_month.getWeekDay() % 7) + 1, date.getMonth() + 1, date.getYear()); 91 | } else { //Go to next year 92 | first_day_next_month = Date(1, (last_day_curr_month.getWeekDay() % 7) + 1, 1, date.getYear() + 1); 93 | } 94 | return Date(date.getMonthDay(), ((date.getMonthDay() % 7) ?: 7) + first_day_next_month.getWeekDay() - 1, first_day_next_month.getMonth(), first_day_next_month.getYear()); 95 | } 96 | 97 | //Assume to get a valid time (i.e. no negative numbers, etc.) 98 | Date DateUtil::decrease_month(Date date) { 99 | Date first_day_curr_month = get_first_day_of_month(date); 100 | Date last_day_prev_month; 101 | if (date.getMonth() > 1) { 102 | last_day_prev_month = Date(get_days_in_month(date.getMonth() - 1, date.getYear()), (first_day_curr_month.getWeekDay() - 1) ?: 7, date.getMonth() - 1, date.getYear()); 103 | } else { //Go to previous year 104 | last_day_prev_month = Date(get_days_in_month(12, date.getYear() - 1), (first_day_curr_month.getWeekDay() - 1) ?: 7, 12, date.getYear() - 1); 105 | } 106 | Date first_day_prev_month = get_first_day_of_month(last_day_prev_month); 107 | return Date(date.getMonthDay(), ((date.getMonthDay() % 7) ?: 7) + first_day_prev_month.getWeekDay() - 1, last_day_prev_month.getMonth(), last_day_prev_month.getYear()); 108 | } 109 | 110 | Date DateUtil::increase_day(Date date) { 111 | Date last_day_curr_month = get_last_day_of_month(date); 112 | if (date.getMonthDay() < last_day_curr_month.getMonthDay()) 113 | return Date(date.getMonthDay() + 1, ((date.getWeekDay() + 1) % 7) ?: 7, date.getMonth(), date.getYear()); 114 | else { //This is the last day of the current month 115 | Date next_month = increase_month(date); 116 | return get_first_day_of_month(next_month); 117 | } 118 | } 119 | 120 | Date DateUtil::decrease_day(Date date) { 121 | if (date.getMonthDay() > 1) 122 | return Date(date.getMonthDay() - 1, (date.getWeekDay() - 1) ?: 7, date.getMonth(), date.getYear()); 123 | else { //This is the first day of the current month 124 | Date previous_month = decrease_month(date); 125 | return get_last_day_of_month(previous_month); 126 | } 127 | } 128 | 129 | Date DateUtil::increase_year(Date date) { 130 | if ((date.getMonthDay() == 29) && (date.getMonth() == 2)) /* Leap year */ 131 | return Date(1, ((date.getWeekDay() + 2) % 7) ?: 7, date.getMonth() + 1, date.getYear() + 1); 132 | else { 133 | int offset = 1; 134 | if (is_leap(date.getYear() + 1)) 135 | offset = 2; 136 | return Date(date.getMonthDay(), ((date.getWeekDay() + offset) % 7) ?: 7, date.getMonth(), date.getYear() + 1); 137 | } 138 | } 139 | 140 | Date DateUtil::decrease_year(Date date) { 141 | int weekday = ((date.getWeekDay() - 1) % 7) ?: 7; 142 | if (is_leap(date.getYear())) 143 | weekday = ((weekday - 1) % 7) ?: 7; 144 | return Date(date.getMonthDay(), weekday, date.getMonth(), date.getYear() - 1); 145 | } 146 | -------------------------------------------------------------------------------- /src/util/dateutil.h: -------------------------------------------------------------------------------- 1 | #ifndef DATEUTIL_H 2 | #define DATEUTIL_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "../model/date.h" 9 | 10 | #define SECONDS_IN_1DAY 86400 11 | 12 | using namespace std; 13 | 14 | class DateUtil 15 | { 16 | public: 17 | DateUtil(); 18 | static Date get_current_date(); 19 | static string get_literal_month(int m); /* 1 => January, ... */ 20 | static string numeric2literal_day_of_week(int d); /* Monday => 1, Tuesday => 2, ... */ 21 | static int literal2numeric_day_of_week(const string &d); /* 1 => Monday, 2 => Tuesday, ... */ 22 | static int get_days_in_month(int month, int year); 23 | static Date date_from_timestamp(time_t timestamp); 24 | static Date get_first_day_of_month(Date &date); 25 | static Date get_last_day_of_month(Date &date); 26 | static Date increase_month(Date date); 27 | static Date decrease_month(Date date); 28 | static Date increase_day(Date date); 29 | static Date decrease_day(Date date); 30 | static Date increase_year(Date date); 31 | static Date decrease_year(Date date); 32 | static bool is_leap(int year); 33 | }; 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /src/util/eventutil.cpp: -------------------------------------------------------------------------------- 1 | #include "eventutil.h" 2 | 3 | #include 4 | 5 | Event* EventUtil::parseString(const string &str_event) { 6 | string str = str_event; 7 | size_t pos = 0; 8 | string token[6]; 9 | string delimiter("##"); 10 | int i = 0; 11 | while ((pos = str.find(delimiter)) != std::string::npos) { 12 | token[i] = str.substr(0, pos); 13 | str.erase(0, pos + delimiter.length()); 14 | i++; 15 | } 16 | if ((i < 5) || (token[0] == "")) return NULL; 17 | token[5] = str; 18 | Category *category = NULL; 19 | SecurePManager spm; 20 | vector categories = spm.get_categories(); 21 | for (Category *c : categories) { 22 | if (token[3] == c->getName()) { 23 | category = new Category(*c); 24 | break; 25 | } 26 | } 27 | return new Event(0,token[0],token[1],token[2],category,stol(token[4]),stol(token[5])); 28 | } 29 | -------------------------------------------------------------------------------- /src/util/eventutil.h: -------------------------------------------------------------------------------- 1 | #ifndef EVENTUTIL_H 2 | #define EVENTUTIL_H 3 | 4 | #include 5 | #include "../model/event.h" 6 | #include "../persistence/securepmanager.h" 7 | 8 | using namespace std; 9 | 10 | class EventUtil 11 | { 12 | public: 13 | static Event *parseString(const string &str_event); 14 | }; 15 | 16 | #endif // EVENTUTIL_H 17 | -------------------------------------------------------------------------------- /src/util/linuxnotifymanager.cpp: -------------------------------------------------------------------------------- 1 | #include "linuxnotifymanager.h" 2 | 3 | #include 4 | 5 | LinuxNotifyManager::LinuxNotifyManager() 6 | { 7 | 8 | } 9 | 10 | /* Send a notification with all the future events starting from the current date to the specified day */ 11 | bool LinuxNotifyManager::notifyEvents(int dayoffset) { 12 | bool ret = true; 13 | PManager pm; 14 | Date current = DateUtil::get_current_date(); 15 | Date untildate = current; 16 | for (int i = 0; i < dayoffset; i++) { 17 | untildate = DateUtil::increase_day(untildate); 18 | } 19 | 20 | list events = pm.get_events_of_month(current.getMonth(), current.getYear()); 21 | if (untildate.getMonth() != current.getMonth()) //if untildate is in the next month, I'll append all the events of the next month 22 | events.splice(events.end(), pm.get_events_of_month(untildate.getMonth(), untildate.getYear())); 23 | for (Event *event : events) { 24 | Date start = DateUtil::date_from_timestamp(event->getStart()); 25 | if ((start.compareTo(current) >= 0) && (start.compareTo(untildate) <= 0)) { 26 | /* Send a notification */ 27 | ret = ret && this->notifyEvent(event); 28 | } 29 | } 30 | return ret; 31 | } 32 | 33 | bool LinuxNotifyManager::notifyEvent(Event *e) { 34 | QProcess process; 35 | Date start = DateUtil::date_from_timestamp(e->getStart()); 36 | Date today = DateUtil::get_current_date(); 37 | Date tomorrow = DateUtil::increase_day(today); 38 | QString title(start.toString(false).c_str()); 39 | if (start == today) 40 | title = QString("Today"); 41 | else if (start == tomorrow) 42 | title = QString("Tomorrow"); 43 | process.start("notify-send", QStringList() << "-i" << ICON_NAME << title << e->getName().c_str()); 44 | return process.waitForFinished(); 45 | } 46 | -------------------------------------------------------------------------------- /src/util/linuxnotifymanager.h: -------------------------------------------------------------------------------- 1 | #ifndef LINUXNOTIFYMANAGER_H 2 | #define LINUXNOTIFYMANAGER_H 3 | 4 | #include 5 | #include "../persistence/pmanager.h" 6 | #include "notifymanager.h" 7 | #include "dateutil.h" 8 | 9 | #define ICON_NAME "appointment-new" /* TODO: choose a better icon */ 10 | 11 | class LinuxNotifyManager : public NotifyManager 12 | { 13 | public: 14 | LinuxNotifyManager(); 15 | bool notifyEvents(int dayoffset); 16 | private: 17 | bool notifyEvent(Event *e); 18 | }; 19 | 20 | #endif // LINUXNOTIFYMANAGER_H 21 | -------------------------------------------------------------------------------- /src/util/notifymanager.h: -------------------------------------------------------------------------------- 1 | #ifndef NOTIFYMANAGER_H 2 | #define NOTIFYMANAGER_H 3 | 4 | 5 | class NotifyManager 6 | { 7 | public: 8 | virtual ~NotifyManager() {} 9 | virtual bool notifyEvents(int day) = 0; 10 | }; 11 | 12 | #endif // NOTIFYMANAGER_H 13 | -------------------------------------------------------------------------------- /src/util/pluginmanager.cpp: -------------------------------------------------------------------------------- 1 | #include "pluginmanager.h" 2 | 3 | PluginManager::PluginManager() 4 | { 5 | experimental::filesystem::path dir(TOOLS_FOLDER); 6 | experimental::filesystem::create_directory(dir); 7 | for (experimental::filesystem::directory_entry e : experimental::filesystem::directory_iterator(TOOLS_FOLDER)) { 8 | experimental::filesystem::path p = e.path(); 9 | if ((p.extension() == ".sh") || (p.extension() == ".bat")) 10 | this->tools.push_back(p.filename()); 11 | } 12 | } 13 | 14 | void PluginManager::runTool(const string &name) { 15 | string cmd = string(TOOLS_FOLDER) + "/\"" + name +"\""; 16 | system(cmd.c_str()); 17 | } 18 | 19 | vector PluginManager::get_tools() { 20 | return this->tools; 21 | } 22 | -------------------------------------------------------------------------------- /src/util/pluginmanager.h: -------------------------------------------------------------------------------- 1 | #ifndef PLUGINMANAGER_H 2 | #define PLUGINMANAGER_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #define TOOLS_FOLDER "tools" 9 | 10 | using namespace std; 11 | 12 | class PluginManager 13 | { 14 | private: 15 | vector tools; 16 | 17 | public: 18 | PluginManager(); 19 | void runTool(const string &name); 20 | vector get_tools(); 21 | }; 22 | 23 | #endif // PLUGINMANAGER_H 24 | -------------------------------------------------------------------------------- /src/view/categorydialog.cpp: -------------------------------------------------------------------------------- 1 | #include "categorydialog.h" 2 | 3 | #include 4 | #include 5 | 6 | CategoryDialog::CategoryDialog(View *parentView, QWidget *parent) : 7 | QDialog(parent), 8 | ui(new Ui::CategoryDialog) 9 | { 10 | this->parent = parentView; 11 | this->setFixedWidth(300); 12 | this->setFixedHeight(400); 13 | this->setWindowTitle("Category Manager"); 14 | this->pm = new PManager; 15 | QVBoxLayout *layout = new QVBoxLayout; 16 | this->list_widget = new QListWidget; 17 | connect(this->list_widget, &QListWidget::itemClicked, this, &CategoryDialog::on_item_click); 18 | load_categories(); 19 | QPushButton *button_add = new QPushButton("&Add new category"); 20 | QPushButton *button_cancel = new QPushButton("&Cancel"); 21 | connect(button_cancel, &QPushButton::clicked, this, &CategoryDialog::on_button_cancel_click); 22 | connect(button_add, &QPushButton::clicked, this, &CategoryDialog::on_button_add_click); 23 | layout->addWidget(this->list_widget); 24 | layout->addWidget(button_add); 25 | layout->addWidget(button_cancel); 26 | this->setLayout(layout); 27 | } 28 | 29 | CategoryDialog::~CategoryDialog() 30 | { 31 | delete ui; 32 | delete pm; 33 | for (Category *category : this->category_list) delete category; 34 | } 35 | 36 | void CategoryDialog::load_categories() { 37 | for (Category *category : this->category_list) delete category; 38 | this->category_list = this->pm->get_categories(); 39 | this->list_widget->clear(); 40 | for (Category *category : this->category_list) { 41 | QPixmap pixmap(ICON_SIZE, ICON_SIZE); 42 | pixmap.fill(QColor(category->getColor().c_str())); 43 | this->list_widget->addItem(new QListWidgetItem(QIcon(pixmap), QString(category->getName().c_str()))); 44 | } 45 | } 46 | 47 | void CategoryDialog::on_item_click() { 48 | CategoryEditDialog *category_edit_dialog = new CategoryEditDialog(this); 49 | category_edit_dialog->show(); 50 | } 51 | 52 | void CategoryDialog::on_button_cancel_click() { 53 | this->close(); 54 | delete this; 55 | } 56 | 57 | void CategoryDialog::on_button_add_click() { 58 | CategoryEditDialog *category_edit_dialog = new CategoryEditDialog(this); 59 | category_edit_dialog->show(); 60 | } 61 | 62 | vector CategoryDialog::getCategoryList() { 63 | return this->category_list; 64 | } 65 | 66 | QListWidget* CategoryDialog::getListWidget() { 67 | return this->list_widget; 68 | } 69 | 70 | PManager* CategoryDialog::getPManager() { 71 | return this->pm; 72 | } 73 | View* CategoryDialog::getParentView() { 74 | return this->parent; 75 | } 76 | -------------------------------------------------------------------------------- /src/view/categorydialog.h: -------------------------------------------------------------------------------- 1 | #ifndef CATEGORYDIALOG_H 2 | #define CATEGORYDIALOG_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "ui_categorydialog.h" 11 | #include "persistence/pmanager.h" 12 | #include "categoryeditdialog.h" 13 | #include "view.h" 14 | 15 | #define DEFAULT_COLOR "#1022A0" 16 | #define ICON_SIZE 10 17 | 18 | namespace Ui { 19 | class CategoryDialog; 20 | } 21 | 22 | class CategoryDialog : public QDialog 23 | { 24 | Q_OBJECT 25 | 26 | private: 27 | Ui::CategoryDialog *ui; 28 | QListWidget *list_widget; 29 | vector category_list; 30 | PManager *pm; 31 | View *parent; 32 | 33 | public: 34 | explicit CategoryDialog(View *parentView, QWidget *parent = 0); 35 | ~CategoryDialog(); 36 | QListWidget *getListWidget(); 37 | vector getCategoryList(); 38 | PManager* getPManager(); 39 | View* getParentView(); 40 | void load_categories(); 41 | 42 | public slots: 43 | void on_button_cancel_click(); 44 | void on_button_add_click(); 45 | void on_item_click(); 46 | }; 47 | 48 | #endif // CATEGORYDIALOG_H 49 | -------------------------------------------------------------------------------- /src/view/categoryeditdialog.cpp: -------------------------------------------------------------------------------- 1 | #include "categoryeditdialog.h" 2 | 3 | CategoryEditDialog::CategoryEditDialog(CategoryDialog *parentDialog, QWidget *parent) : 4 | QDialog(parent), 5 | ui(new Ui::CategoryEditDialog) 6 | { 7 | this->parentDialog = parentDialog; 8 | Category *selected_category = NULL; 9 | if (this->parentDialog->getListWidget()->selectedItems().size() != 0) 10 | selected_category = this->parentDialog->getCategoryList()[this->parentDialog->getListWidget()->currentIndex().row()]; 11 | QVBoxLayout *vl = new QVBoxLayout; 12 | QHBoxLayout *hl = new QHBoxLayout; 13 | QHBoxLayout *hl2 = new QHBoxLayout; 14 | button_color = new QPushButton; 15 | if (selected_category != NULL) 16 | this->selected_color = QColor(selected_category->getColor().c_str()); 17 | else 18 | this->selected_color = QColor(DEFAULT_COLOR); 19 | QPixmap pixmap(ICON_SIZE, ICON_SIZE); 20 | pixmap.fill(this->selected_color); 21 | button_color->setIcon(QIcon(pixmap)); 22 | edit_name = new QLineEdit; 23 | if (selected_category != NULL) 24 | edit_name->setText(selected_category->getName().c_str()); 25 | else 26 | edit_name->setPlaceholderText("Type a name for the new category"); 27 | hl->addWidget(edit_name); 28 | hl->addWidget(button_color); 29 | connect(button_color, &QPushButton::clicked, this, &CategoryEditDialog::on_button_color_click); 30 | QPushButton *button_save = new QPushButton("&Save"); 31 | QPushButton *button_delete = new QPushButton("&Delete"); 32 | connect(button_delete, &QPushButton::clicked, this, &CategoryEditDialog::on_button_delete_click); 33 | connect(button_save, &QPushButton::clicked, this, &CategoryEditDialog::on_button_save_click); 34 | hl2->addWidget(button_save); 35 | hl2->addWidget(button_delete); 36 | vl->addLayout(hl); 37 | vl->addLayout(hl2); 38 | this->setModal(true); 39 | this->setWindowFlags(Qt::WindowCloseButtonHint); 40 | this->setWindowTitle("Category Edit Dialog"); 41 | setLayout(vl); 42 | } 43 | 44 | CategoryEditDialog::~CategoryEditDialog() { 45 | delete this->ui; 46 | } 47 | 48 | void CategoryEditDialog::on_button_save_click() { 49 | if(this->edit_name->text().length() < 3) { 50 | QMessageBox::critical(this, "Error", "The name must have a length greater than 2", QMessageBox::Ok); 51 | return; 52 | } 53 | Category category(0, this->edit_name->text().toStdString(), this->selected_color.name().toStdString()); 54 | //Create new category 55 | if ((this->parentDialog->getListWidget()->selectedItems().size() == 0) && (this->parentDialog->getPManager()->add_category(&category))) { 56 | this->parentDialog->load_categories(); 57 | this->close(); 58 | delete this; 59 | }else //Change the selected category 60 | if (this->parentDialog->getPManager()->replace_category(this->parentDialog->getCategoryList()[this->parentDialog->getListWidget()->currentIndex().row()], &category)) { 61 | this->parentDialog->load_categories(); 62 | this->parentDialog->getParentView()->refresh_events(); 63 | this->close(); 64 | delete this; 65 | } else { 66 | QMessageBox::critical(this, "Error", "Persistence error. Try with a different name or color."); 67 | } 68 | } 69 | 70 | void CategoryEditDialog::on_button_delete_click() { 71 | if (this->parentDialog->getListWidget()->selectedItems().size() == 0) { 72 | QMessageBox::critical(this, "Error", "No category selected", QMessageBox::Ok); 73 | return; 74 | } 75 | QString selected_category = this->parentDialog->getListWidget()->selectedItems().at(0)->text(); 76 | QMessageBox::StandardButton reply; 77 | reply = QMessageBox::question(this, "Confirm", QString("Do you want to delete ") + selected_category + QString("?"), QMessageBox::Yes|QMessageBox::No); 78 | if (reply == QMessageBox::Yes) { 79 | if (!this->parentDialog->getPManager()->remove_category(this->parentDialog->getCategoryList()[this->parentDialog->getListWidget()->currentIndex().row()])) 80 | QMessageBox::critical(this, "Error", "Persistence error. You can't remove a category used by events. First remove these events and then try again."); 81 | else { 82 | this->parentDialog->load_categories(); 83 | this->close(); 84 | delete this; 85 | } 86 | } 87 | } 88 | 89 | void CategoryEditDialog::on_button_color_click() { 90 | QColor color = QColorDialog::getColor(QColor(this->selected_color)); 91 | if (color.isValid()) { 92 | this->selected_color = color; 93 | QPixmap pixmap(ICON_SIZE, ICON_SIZE); 94 | pixmap.fill(QColor(this->selected_color)); 95 | button_color->setIcon(QIcon(pixmap)); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/view/categoryeditdialog.h: -------------------------------------------------------------------------------- 1 | #ifndef CATEGORYEDITDIALOG_H 2 | #define CATEGORYEDITDIALOG_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "ui_categoryeditdialog.h" 11 | #include "model/category.h" 12 | #include "categorydialog.h" 13 | 14 | //We need this forward declaration to avoid circular dependencies among headers 15 | class CategoryDialog; 16 | 17 | namespace Ui { 18 | class CategoryEditDialog; 19 | } 20 | 21 | class CategoryEditDialog : public QDialog 22 | { 23 | Q_OBJECT 24 | 25 | private: 26 | Ui::CategoryEditDialog *ui; 27 | CategoryDialog *parentDialog; 28 | QColor selected_color; 29 | QLineEdit *edit_name; 30 | QPushButton *button_color; 31 | 32 | public: 33 | explicit CategoryEditDialog(CategoryDialog *parentDialog, QWidget *parent = 0); 34 | ~CategoryEditDialog(); 35 | 36 | public slots: 37 | void on_button_delete_click(); 38 | void on_button_save_click(); 39 | void on_button_color_click(); 40 | }; 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /src/view/categoryselectdialog.cpp: -------------------------------------------------------------------------------- 1 | #include "categoryselectdialog.h" 2 | 3 | #include 4 | 5 | CategorySelectDialog::CategorySelectDialog(View *parentView, QString text, QWidget *parent) : 6 | QDialog(parent), 7 | ui(new Ui::CategorySelectDialog) 8 | { 9 | this->parent = parentView; 10 | this->setWindowTitle("Category Selector"); 11 | this->pm = new PManager; 12 | this->selected_category = NULL; 13 | QHBoxLayout *button_layout = new QHBoxLayout; 14 | QVBoxLayout *main_layout = new QVBoxLayout; 15 | this->list_categories = new QComboBox; 16 | load_categories(); 17 | QPushButton *button_ok = new QPushButton("OK"); 18 | QPushButton *button_cancel = new QPushButton("Cancel"); 19 | button_cancel->setFixedWidth(50); 20 | button_ok->setFixedWidth(50); 21 | connect(button_cancel, &QPushButton::clicked, this, &CategorySelectDialog::on_button_cancel_click); 22 | connect(button_ok, &QPushButton::clicked, this, &CategorySelectDialog::on_button_ok_click); 23 | QLabel *message = new QLabel(text); 24 | main_layout->addWidget(message); 25 | main_layout->addWidget(this->list_categories); 26 | button_layout->addWidget(button_ok); 27 | button_layout->addWidget(button_cancel); 28 | main_layout->addLayout(button_layout); 29 | this->setLayout(main_layout); 30 | } 31 | 32 | void CategorySelectDialog::load_categories() { 33 | for (Category *category : this->category_list) delete category; 34 | this->category_list = this->pm->get_categories(); 35 | for (Category *category : category_list) { 36 | QPixmap pixmap(10, 10); 37 | pixmap.fill(QColor(category->getColor().c_str())); 38 | this->list_categories->addItem(QIcon(pixmap), QString(category->getName().c_str())); 39 | } 40 | } 41 | 42 | void CategorySelectDialog::on_button_cancel_click() { 43 | this->selected_category = NULL; 44 | this->close(); 45 | delete this; 46 | } 47 | 48 | void CategorySelectDialog::on_button_ok_click() { 49 | for (Category *c : this->category_list) { 50 | if (this->list_categories->currentText().toStdString() == c->getName()) { 51 | this->selected_category = new Category(*c); 52 | break; 53 | } 54 | } 55 | this->close(); 56 | delete this; 57 | } 58 | 59 | Category* CategorySelectDialog::getSelectedCategory() { 60 | return this->selected_category; 61 | } 62 | 63 | CategorySelectDialog::~CategorySelectDialog() 64 | { 65 | delete ui; 66 | delete pm; 67 | for (Category *category : this->category_list) delete category; 68 | } 69 | -------------------------------------------------------------------------------- /src/view/categoryselectdialog.h: -------------------------------------------------------------------------------- 1 | #ifndef CATEGORYSELECTDIALOG_H 2 | #define CATEGORYSELECTDIALOG_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "ui_categoryselectdialog.h" 11 | #include "view.h" 12 | #include "persistence/pmanager.h" 13 | #include "categorydialog.h" 14 | 15 | namespace Ui { 16 | class CategorySelectDialog; 17 | } 18 | 19 | class CategorySelectDialog : public QDialog 20 | { 21 | Q_OBJECT 22 | 23 | private: 24 | Ui::CategorySelectDialog *ui; 25 | QComboBox *list_categories; 26 | vector category_list; 27 | PManager *pm; 28 | View *parent; 29 | Category *selected_category; 30 | 31 | public: 32 | explicit CategorySelectDialog(View *parentView, QString text, QWidget *parent = 0); 33 | ~CategorySelectDialog(); 34 | void load_categories(); 35 | Category *getSelectedCategory(); 36 | 37 | public slots: 38 | void on_button_cancel_click(); 39 | void on_button_ok_click(); 40 | }; 41 | 42 | #endif // CATEGORYSELECTDIALOG_H 43 | -------------------------------------------------------------------------------- /src/view/customdialog.cpp: -------------------------------------------------------------------------------- 1 | #include "customdialog.h" 2 | 3 | #include 4 | 5 | CustomDialog::CustomDialog(QLayout *layout, QWidget *parent) : 6 | QDialog(parent), 7 | ui(new Ui::CustomDialog) 8 | { 9 | this->setWindowFlags(Qt::WindowCloseButtonHint | Qt::WindowMinimizeButtonHint); 10 | setLayout(layout); 11 | } 12 | 13 | CustomDialog::~CustomDialog() 14 | { 15 | delete ui; 16 | } 17 | 18 | void CustomDialog::keyPressEvent(QKeyEvent *e) { 19 | if(e->key() != Qt::Key_Escape) 20 | QDialog::keyPressEvent(e); 21 | else { 22 | this->close(); 23 | delete this; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/view/customdialog.h: -------------------------------------------------------------------------------- 1 | #ifndef CUSTOMDIALOG_H 2 | #define CUSTOMDIALOG_H 3 | 4 | #include "ui_customdialog.h" 5 | #include 6 | #include 7 | #include 8 | 9 | namespace Ui { 10 | class CustomDialog; 11 | } 12 | 13 | class CustomDialog : public QDialog 14 | { 15 | Q_OBJECT 16 | 17 | public: 18 | explicit CustomDialog(QLayout *layout, QWidget *parent = 0); 19 | ~CustomDialog(); 20 | 21 | protected: 22 | virtual void keyPressEvent(QKeyEvent *e); 23 | 24 | private: 25 | Ui::CustomDialog *ui; 26 | }; 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /src/view/eventdialog.cpp: -------------------------------------------------------------------------------- 1 | #include "eventdialog.h" 2 | 3 | #include 4 | 5 | void EventDialog::setEvent(Event *event) { 6 | this->everyMonth->setEnabled(false); /* Not supported for now */ 7 | this->everyYear->setEnabled(false); 8 | this->event = event; 9 | if (event != NULL) { 10 | this->edit_name->setText(event->getName().c_str()); 11 | this->edit_description->setPlainText(event->getDescription().c_str()); 12 | this->edit_place->setText(event->getPlace().c_str()); 13 | int index = 0; 14 | for (Category *category : category_list) { 15 | if (category->getName() == event->getCategory()->getName()) 16 | this->edit_category->setCurrentIndex(index); 17 | index++; 18 | } 19 | 20 | if (event->getStart() == TODO_DATE) { 21 | Date today = DateUtil::get_current_date(); 22 | /* Set the current date, so if the user uncheck the "TODO" there is already a valid date set */ 23 | this->edit_start->setDateTime(QDateTime(QDate(today.getYear(), today.getMonth(), today.getMonthDay()))); 24 | this->edit_start->setEnabled(false); 25 | this->edit_end->setDateTime(QDateTime(QDate(today.getYear(), today.getMonth(), today.getMonthDay()))); 26 | this->edit_end->setEnabled(false); 27 | this->cbtodo->setChecked(true); 28 | } else { 29 | this->edit_start->setDateTime(QDateTime::fromTime_t(event->getStart())); 30 | this->edit_end->setDateTime(QDateTime::fromTime_t(event->getEnd())); 31 | } 32 | this->button_delete->setEnabled(true); 33 | } 34 | } 35 | 36 | EventDialog::EventDialog(View *parentView, Date start_date, Date end_date, QWidget *parent) : 37 | QDialog(parent), 38 | ui(new Ui::EventDialog) 39 | { 40 | this->parent = parentView; 41 | this->setFixedWidth(400); 42 | this->setFixedHeight(500); 43 | this->setModal(true); 44 | this->setWindowTitle("Event Manager"); 45 | 46 | this->pm = new SecurePManager; 47 | this->event = NULL; 48 | 49 | QVBoxLayout *main_layout = new QVBoxLayout; 50 | QHBoxLayout *first_row = new QHBoxLayout; 51 | QLabel *label_name = new QLabel("Name: "); 52 | this->edit_name = new QLineEdit; 53 | this->edit_name->setMaxLength(100); 54 | first_row->addWidget(label_name); 55 | first_row->addWidget(this->edit_name); 56 | main_layout->addLayout(first_row); 57 | QHBoxLayout *second_row = new QHBoxLayout; 58 | QLabel *label_place = new QLabel("Place: "); 59 | this->edit_place = new QLineEdit; 60 | second_row->addWidget(label_place); 61 | second_row->addWidget(this->edit_place); 62 | main_layout->addLayout(second_row); 63 | main_layout->addWidget(new QLabel("Description: ")); 64 | this->edit_description = new QPlainTextEdit; 65 | main_layout->addWidget(this->edit_description); 66 | QHBoxLayout *third_row = new QHBoxLayout; 67 | QLabel *label_category = new QLabel("Category: "); 68 | this->edit_category = new QComboBox; 69 | this->category_list = this->pm->get_categories(); 70 | for (Category *category : category_list) { 71 | QPixmap pixmap(10, 10); 72 | pixmap.fill(QColor(category->getColor().c_str())); 73 | this->edit_category->addItem(QIcon(pixmap), QString(category->getName().c_str())); 74 | } 75 | third_row->addWidget(label_category); 76 | third_row->addWidget(this->edit_category); 77 | main_layout->addLayout(third_row); 78 | QHBoxLayout *fourth_row = new QHBoxLayout; 79 | QLabel *label_todo = new QLabel("TODO: "); 80 | this->cbtodo = new QCheckBox; 81 | connect(cbtodo, &QCheckBox::toggled, this, &EventDialog::on_checkbox_todo_toggle); 82 | fourth_row->addWidget(label_todo); 83 | fourth_row->addWidget(cbtodo); 84 | main_layout->addLayout(fourth_row); 85 | QHBoxLayout *fifth_row = new QHBoxLayout; 86 | QLabel *label_start = new QLabel("Start: "); 87 | this->edit_start = new QDateTimeEdit; 88 | this->edit_start->setCalendarPopup(true); 89 | QDateTime todoDate = QDateTime::fromTime_t(TODO_DATE); 90 | this->edit_start->setDateTime(QDateTime(QDate(start_date.getYear(), start_date.getMonth(), start_date.getMonthDay()))); 91 | //I set a specific hour because the default is the midnight, but this could lead more easily problems caused by daylight saving time 92 | this->edit_start->setTime(QTime(8,0,0)); 93 | connect(this->edit_start, &QDateTimeEdit::dateTimeChanged, this, &EventDialog::on_date_start_change); 94 | fifth_row->addWidget(label_start); 95 | fifth_row->addWidget(this->edit_start); 96 | main_layout->addLayout(fifth_row); 97 | QHBoxLayout *sixth_row = new QHBoxLayout; 98 | QLabel *label_end = new QLabel("End: "); 99 | this->edit_end = new QDateTimeEdit; 100 | this->edit_end->setCalendarPopup(true); 101 | this->edit_end->setDateTime(QDateTime(QDate(end_date.getYear(), end_date.getMonth(), end_date.getMonthDay()))); 102 | this->edit_end->setTime(QTime(9,0,0)); 103 | sixth_row->addWidget(label_end); 104 | sixth_row->addWidget(this->edit_end); 105 | main_layout->addLayout(sixth_row); 106 | QHBoxLayout *seventh_row = new QHBoxLayout; 107 | QLabel *label_recurrent = new QLabel("Recurrence: "); 108 | this->everyMonth = new QRadioButton("Monthly"); 109 | this->everyYear = new QRadioButton("Yearly"); 110 | this->options = new QGroupBox; 111 | seventh_row->addWidget(label_recurrent); 112 | seventh_row->addWidget(this->everyMonth); 113 | seventh_row->addWidget(this->everyYear); 114 | options->setLayout(seventh_row); 115 | main_layout->addWidget(options); 116 | QHBoxLayout *last_row = new QHBoxLayout; 117 | QPushButton *button_cancel = new QPushButton("&Cancel"); 118 | connect(button_cancel, &QPushButton::clicked, this, &EventDialog::on_button_cancel_click); 119 | button_delete = new QPushButton("&Delete"); 120 | button_delete->setEnabled(false); 121 | connect(button_delete, &QPushButton::clicked, this, &EventDialog::on_button_delete_click); 122 | QPushButton *button_save = new QPushButton("&Save"); 123 | connect(button_save, &QPushButton::clicked, this, &EventDialog::on_button_save_click); 124 | last_row->addWidget(button_cancel); 125 | last_row->addWidget(button_delete); 126 | last_row->addWidget(button_save); 127 | main_layout->addLayout(last_row); 128 | 129 | this->setLayout(main_layout); 130 | } 131 | 132 | EventDialog::~EventDialog() 133 | { 134 | delete ui; 135 | delete this->pm; 136 | for (Category *c : this->category_list) delete c; 137 | } 138 | 139 | void EventDialog::on_button_cancel_click() { 140 | this->close(); 141 | delete this; 142 | } 143 | 144 | void EventDialog::on_button_delete_click() { 145 | this->pm->remove_event(this->event); 146 | this->event->setInvalid(); 147 | refresh(); 148 | } 149 | 150 | void EventDialog::refresh() { 151 | this->parent->refresh_events(); 152 | this->close(); 153 | delete this; 154 | } 155 | 156 | void EventDialog::on_button_save_click() { 157 | QProgressBar *bar = new QProgressBar; 158 | bool ret = true; 159 | bool isTodo = !this->edit_start->isEnabled(); 160 | 161 | if (this->edit_name->text().length() < 3) { 162 | QMessageBox::critical(this, "Error", "The name must have a length greater than 2", QMessageBox::Ok); 163 | return; 164 | } 165 | 166 | if (isTodo) { 167 | QDateTime todoDate = QDateTime::fromTime_t(TODO_DATE); 168 | this->edit_start->setDateTime(todoDate); 169 | this->edit_end->setDateTime(todoDate); 170 | } else { 171 | if (this->edit_start->dateTime() > this->edit_end->dateTime()) { 172 | QMessageBox::critical(this, "Error", "Invalid range of time selected", QMessageBox::Ok); 173 | return; 174 | } 175 | } 176 | 177 | Category *category = NULL; 178 | for (Category *c : this->category_list) { 179 | if (this->edit_category->currentText().toStdString() == c->getName()) { 180 | category = new Category(*c); 181 | break; 182 | } 183 | } 184 | 185 | QDateTime start = this->edit_start->dateTime(); 186 | QDateTime end = this->edit_end->dateTime(); 187 | Event *newEvent = new Event(0, this->edit_name->text().toStdString(), this->edit_description->toPlainText().toStdString(), this->edit_place->text().toStdString(), category, start.toTime_t(), end.toTime_t()); 188 | 189 | /* If the users has changed an existent event, I'll call the right function */ 190 | if ((this->event != NULL) && (this->pm->replace_event(this->event, newEvent))) { 191 | refresh(); 192 | this->event = new Event(*newEvent); 193 | } else if ((this->event == NULL) && (this->everyMonth->isChecked() || this->everyYear->isChecked())) { 194 | int reply = QMessageBox::warning(this, "Attention", "A recurrent event is considered as multiple independent events, after this operation you can modify it only as a single event.", QMessageBox::Yes | QMessageBox::No); 195 | if (reply == QMessageBox::Yes) { 196 | int offset = 8; /* Add the same event to the next 8 years */ 197 | if (this->everyMonth->isChecked()) offset = 24; /* Add the same event to every month of the next 2 years */ 198 | bar->setRange(0, offset); 199 | this->layout()->addWidget(bar); 200 | Event *previous = NULL; 201 | for (int i = 0; i < offset; i++) { 202 | ret = ret && this->pm->add_event(newEvent, previous); 203 | if (previous != NULL) delete previous; 204 | previous = newEvent; 205 | if (!ret) break; 206 | if (this->everyMonth->isChecked()) { 207 | start = start.addMonths(1); 208 | end = end.addMonths(1); 209 | } else { 210 | start = start.addYears(1); 211 | end = end.addYears(1); 212 | } 213 | newEvent = new Event(0, this->edit_name->text().toStdString(), this->edit_description->toPlainText().toStdString(), this->edit_place->text().toStdString(), category, start.toTime_t(), end.toTime_t()); 214 | bar->setValue(i); 215 | QCoreApplication::processEvents(); 216 | } 217 | delete previous; 218 | if (ret) { 219 | delete newEvent; 220 | refresh(); 221 | } 222 | } 223 | } else if ((this->event == NULL) && (this->pm->add_event(newEvent))) { //else I'll create a new Event 224 | delete newEvent; 225 | refresh(); 226 | } else 227 | ret = false; 228 | 229 | if (!ret) { 230 | QMessageBox::critical(this, "Error", "Persistence error. Try with a different name.", QMessageBox::Ok); 231 | delete bar; 232 | } 233 | } 234 | 235 | void EventDialog::on_checkbox_todo_toggle(bool checked) { 236 | this->edit_start->setEnabled(!checked); 237 | this->edit_end->setEnabled(!checked); 238 | this->options->setEnabled(!checked); 239 | } 240 | 241 | void EventDialog::on_date_start_change() { 242 | if (this->edit_start->date() > this->edit_end->date()) 243 | this->edit_end->setDate(this->edit_start->date()); 244 | if (this->edit_start->time() > this->edit_end->time()) 245 | this->edit_end->setTime(this->edit_start->time().addSecs(60*60)); 246 | } 247 | 248 | Event* EventDialog::getEvent() { 249 | return this->event; 250 | } 251 | -------------------------------------------------------------------------------- /src/view/eventdialog.h: -------------------------------------------------------------------------------- 1 | #ifndef EVENTDIALOG_H 2 | #define EVENTDIALOG_H 3 | 4 | #include "ui_eventdialog.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include "util/dateutil.h" 23 | #include "view.h" 24 | #include "persistence/securepmanager.h" 25 | 26 | namespace Ui { 27 | class EventDialog; 28 | } 29 | 30 | class EventDialog : public QDialog 31 | { 32 | Q_OBJECT 33 | 34 | private: 35 | Ui::EventDialog *ui; 36 | QLineEdit *edit_name; 37 | QLineEdit *edit_place; 38 | QComboBox *edit_category; 39 | QDateTimeEdit *edit_start; 40 | QDateTimeEdit *edit_end; 41 | QPlainTextEdit *edit_description; 42 | QPushButton *button_delete; 43 | QCheckBox *cbtodo; 44 | QRadioButton *everyMonth; 45 | QRadioButton *everyYear; 46 | QGroupBox *options; 47 | View *parent; 48 | PManager *pm; 49 | Event *event; 50 | vector category_list; 51 | void refresh(); 52 | 53 | public: 54 | explicit EventDialog(View *parentView, Date start_date = Date(), Date end_date = Date(), QWidget *parent = 0); 55 | ~EventDialog(); 56 | void setEvent(Event *event); 57 | Event* getEvent(); 58 | 59 | public slots: 60 | void on_button_cancel_click(); 61 | void on_button_save_click(); 62 | void on_button_delete_click(); 63 | void on_date_start_change(); 64 | void on_checkbox_todo_toggle(bool checked); 65 | }; 66 | 67 | #endif // EVENTDIALOG_H 68 | -------------------------------------------------------------------------------- /src/view/monthview.h: -------------------------------------------------------------------------------- 1 | #ifndef MONTHVIEW_H 2 | #define MONTHVIEW_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include "ui_monthview.h" 21 | #include "view/eventdialog.h" 22 | #include "view/qframe_extended.h" 23 | #include "view/qwidget_extended.h" 24 | #include "view/categorydialog.h" 25 | #include "view.h" 26 | #include "util/dateutil.h" 27 | #include "qlabel_event.h" 28 | #include "qpushbutton_extended.h" 29 | #include "view/customdialog.h" 30 | #include "view/categoryselectdialog.h" 31 | #include "util/pluginmanager.h" 32 | #include "view/settings.h" 33 | 34 | /* Gets the current month displayed using an hack. Infact, the cell in the middle will have always a value setted. 35 | * This should be used when you don't care about the day */ 36 | #define CURRENT_MONTH *this->frames[21]->getDate() 37 | 38 | /* Style Sheets */ 39 | #define MAINWINDOW_STYLE "#mainwindow { background-color: #FFFFFF; border: 2px solid #DDDDFF; }" 40 | 41 | #define LABELDATE_STYLE "QLabel { color: #000000; qproperty-alignment: AlignCenter; padding-left: 100px; padding-right: 100px; font-size: 20px; }" 42 | 43 | #define TODOSBUTTON_STYLE "QPushButton { border: 1px solid #000000; color: #000000; padding: 5px; font-weight: bold; border-radius: 10px; } QPushButton:hover { color: #2222BB; }" 44 | 45 | #define CELL_STYLE "QFrame { background-color: #FFFFFF; color: #000000; border: 1px outset #CCCCCC; }" \ 46 | "QFrame#holiday { background-color: #FFF5F5; }" \ 47 | "QLabel { border: none; font-size: 16px; padding: 5px; background-color:rgba(0,0,0,0); }" \ 48 | "QLabel#today { background-color: #FFFF88; color: #FF0000; border: 1px solid #FF0000;}" \ 49 | "QFrame#selected { background-color: #EEEEFF; }" \ 50 | "QLabel#header { font-weight: bold; }" \ 51 | "QFrame#header { background-color: #DDDDFF; }" \ 52 | "QFrame#disabled { background-color: #EFEFEF; }" 53 | 54 | namespace Ui { 55 | class MonthView; 56 | } 57 | 58 | class MonthView : public QMainWindow, public View 59 | { 60 | Q_OBJECT 61 | 62 | private: 63 | QSettings settings; 64 | Category *selected_category; 65 | PluginManager *plm; 66 | QVBoxLayout *layout; 67 | QLabel *label_date; 68 | QLabelEvent *selected_event; 69 | QPushButton *todobutton; 70 | Date *selection_start; 71 | Date *selection_end; 72 | Ui::MonthView *ui; 73 | PManager *pm; 74 | QFrameExtended *frames[42]; //I have a 7x7 grid, but without consider the first row I've a total of 6x7 cells 75 | QMenu *dbMenu; 76 | void display_days(Date date); 77 | void remove_events_from_all_frames(); 78 | void remove_events_from_frame(int i); 79 | QLabelEvent *createLabelEvent(Event *event); 80 | QFrameExtended *createQFrameExtended(Date *date); 81 | void createMenu(); 82 | void on_button_extended_click(int index); 83 | CustomDialog *show_progress_bar(QString title); 84 | void refresh_db_menu(); 85 | 86 | protected: 87 | void contextMenuEvent(QContextMenuEvent *event) Q_DECL_OVERRIDE; 88 | 89 | public: 90 | explicit MonthView(QWidget *parent = 0); 91 | void refresh_events(); 92 | void refresh_todos(); 93 | void display_events(Date date, Category *category = NULL); 94 | ~MonthView(); 95 | 96 | public slots: 97 | void on_mouse_press(QFrameExtended *frame); 98 | void on_mouse_release(QFrameExtended *frame); 99 | void on_mouse_move(QFrameExtended *frame); 100 | void on_back_button_click(); 101 | void on_next_button_click(); 102 | void on_event_click(QLabelEvent *label_event, Qt::MouseButton button); 103 | void on_event_key_press(int key); 104 | void on_todo_button_click(); 105 | void keyPressEvent(QKeyEvent* e); 106 | 107 | 108 | private slots: 109 | void exit(); 110 | void delete_db(); 111 | void save_database(); 112 | void load_database(); 113 | void import_events(); 114 | void export_events(); 115 | void add_event(); 116 | void edit_categories(); 117 | void show_agenda(bool only_todos = false); 118 | void show_monthview(); 119 | void filter_by_category(); 120 | void run_tool(string tool); 121 | void switch_db(string db); 122 | void create_database(); 123 | void show_settings(); 124 | }; 125 | 126 | #endif // MONTHVIEW_H 127 | -------------------------------------------------------------------------------- /src/view/qframe_extended.h: -------------------------------------------------------------------------------- 1 | #ifndef QFRAMEEXTENDED_H 2 | #define QFRAMEEXTENDED_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include "util/dateutil.h" 8 | 9 | class QFrameExtended : public QFrame 10 | { 11 | Q_OBJECT 12 | 13 | private: 14 | Date *date; 15 | 16 | public: 17 | explicit QFrameExtended(QWidget *parent = 0) : QFrame(parent), date(NULL) { } 18 | ~QFrameExtended() { delete this->date; } 19 | Date* getDate() { return date; } 20 | void setDate(Date *d) { 21 | if (this->date != NULL) delete this->date; 22 | this->date = d; 23 | } 24 | 25 | }; 26 | 27 | #endif // QFRAMEEXTENDED_H 28 | -------------------------------------------------------------------------------- /src/view/qlabel_event.cpp: -------------------------------------------------------------------------------- 1 | #include "qlabel_event.h" 2 | 3 | #include 4 | 5 | QLabelEvent::QLabelEvent(QWidget *parent) : QLabel(parent) 6 | { 7 | this->event = NULL; 8 | } 9 | 10 | QLabelEvent::~QLabelEvent() { 11 | if (this->event != NULL) 12 | delete this->event; 13 | } 14 | 15 | void QLabelEvent::mousePressEvent(QMouseEvent *e) { 16 | emit clicked(this, e->button()); 17 | } 18 | 19 | void QLabelEvent::keyPressEvent(QKeyEvent *e) { 20 | emit keyPressed(e->key()); 21 | } 22 | 23 | void QLabelEvent::setEvent(Event *event) { 24 | if (this->event != NULL) delete this->event; 25 | this->event = event; 26 | this->drawUI(); 27 | } 28 | 29 | Event* QLabelEvent::getEvent() { 30 | return this->event; 31 | } 32 | 33 | bool QLabelEvent::markSelection(bool selected) { 34 | if (this->event == NULL) return false; 35 | if (selected) 36 | this->setObjectName("selected"); 37 | else 38 | this->setObjectName(""); 39 | //Update the ui 40 | this->setStyleSheet(this->styleSheet()); 41 | return true; 42 | } 43 | 44 | bool QLabelEvent::drawInvalidEvent() { 45 | if (this->event == NULL) return false; 46 | this->setText(QString("") + QString(this->event->getName().c_str()) + QString("")); 47 | this->setStyleSheet(QString("QLabel { font-size: 14px; background-color : #AAAAAA; color: #000000; };")); 48 | this->setFixedHeight(26); 49 | this->setMargin(0); 50 | this->setEnabled(false); 51 | return true; 52 | } 53 | 54 | bool QLabelEvent::drawUI() { 55 | if (this->event == NULL) return false; 56 | if (this->event->isInvalid()) return drawInvalidEvent(); 57 | 58 | this->setText(this->event->getName().c_str()); 59 | QString textColor("#000000"); 60 | if (is_color_dark(this->event->getCategory()->getColor())) 61 | textColor = "#FFFFFF"; 62 | this->setStyleSheet(QString("QLabel#selected {border-top: 1px solid #FF0000; border-bottom: 1px solid #FF0000; font-weight: bold; font-size: 13px;} QLabel { font-size: 14px; border-radius: 2px; background-color : ") + QString(this->event->getCategory()->getColor().c_str()) + QString("; color: ") + textColor + QString("};")); 63 | this->setFixedHeight(26); 64 | this->setMargin(0); 65 | QString tooltip_text; 66 | if (this->event->getName().length() > 20) 67 | tooltip_text = QString("Name: ") + this->event->getName().c_str() + QString("\n"); 68 | if (this->event->getPlace() != "") 69 | tooltip_text = tooltip_text + QString("Place: ") + this->event->getPlace().c_str() + QString("\n"); 70 | if (this->event->getDescription() != "") 71 | tooltip_text = tooltip_text + QString("Description: ") + this->event->getDescription().c_str(); 72 | this->setToolTip(tooltip_text); 73 | return true; 74 | } 75 | 76 | /* This is an utility function and should be moved in another class, but for the moment no one else is using it. */ 77 | bool QLabelEvent::is_color_dark(string colorName) { 78 | QColor color(colorName.c_str()); 79 | //Formula to calculate luminance from ITU-R BT.709 80 | int l = 0.2126 * color.red() + 0.7152 * color.green() + 0.0722 * color.blue(); 81 | if (l < 50) 82 | return true; 83 | else 84 | return false; 85 | } 86 | 87 | -------------------------------------------------------------------------------- /src/view/qlabel_event.h: -------------------------------------------------------------------------------- 1 | #ifndef QLABELEVENT_H 2 | #define QLABELEVENT_H 3 | 4 | #include 5 | #include 6 | #include "model/event.h" 7 | 8 | class QLabelEvent : public QLabel 9 | { 10 | Q_OBJECT 11 | private: 12 | Event *event; 13 | 14 | public: 15 | explicit QLabelEvent(QWidget *parent = 0); 16 | ~QLabelEvent(); 17 | bool markSelection(bool selected); 18 | void setEvent(Event *event); 19 | Event* getEvent(); 20 | bool drawUI(); 21 | bool is_color_dark(string colorName); 22 | 23 | private: 24 | bool drawInvalidEvent(); 25 | 26 | protected: 27 | virtual void mousePressEvent(QMouseEvent *e); 28 | virtual void keyPressEvent(QKeyEvent *e); 29 | 30 | signals: 31 | void clicked(QLabelEvent *event, Qt::MouseButton button); 32 | void keyPressed(int key); 33 | 34 | public slots: 35 | 36 | }; 37 | 38 | #endif // QLABELEVENT_H 39 | -------------------------------------------------------------------------------- /src/view/qpushbutton_extended.cpp: -------------------------------------------------------------------------------- 1 | #include "qpushbutton_extended.h" 2 | 3 | #include 4 | 5 | QPushButtonExtended::QPushButtonExtended(const char *text, QWidget *parent) : QPushButton(text, parent) 6 | { 7 | //clicked can't be overwritten 8 | connect(this, &QPushButtonExtended::clicked, this, &QPushButtonExtended::button_clicked); 9 | } 10 | 11 | void QPushButtonExtended::button_clicked() { 12 | emit on_click(this->index); 13 | } 14 | 15 | void QPushButtonExtended::setIndex(int index) { 16 | this->index = index; 17 | } 18 | -------------------------------------------------------------------------------- /src/view/qpushbutton_extended.h: -------------------------------------------------------------------------------- 1 | #ifndef QPUSHBUTTONEXTENDED_H 2 | #define QPUSHBUTTONEXTENDED_H 3 | 4 | #include 5 | 6 | class QPushButtonExtended : public QPushButton 7 | { 8 | Q_OBJECT 9 | private: 10 | int index; 11 | 12 | public: 13 | explicit QPushButtonExtended(const char *text, QWidget *parent = 0); 14 | void setIndex(int index); 15 | void button_clicked(); 16 | 17 | signals: 18 | void on_click(int index); 19 | 20 | public slots: 21 | }; 22 | 23 | #endif // QPUSHBUTTONEXTENDED_H 24 | -------------------------------------------------------------------------------- /src/view/qwidget_extended.cpp: -------------------------------------------------------------------------------- 1 | #include "qwidget_extended.h" 2 | #include "qframe_extended.h" 3 | #include 4 | 5 | QWidgetExtended::QWidgetExtended(QWidget *parent) : QWidget(parent) 6 | { 7 | 8 | } 9 | 10 | void QWidgetExtended::signalEvent(QMouseEvent *event, int code) { 11 | QFrameExtended frame; 12 | QWidget *widget = this->childAt(event->pos()); 13 | if (widget != NULL) { 14 | QString widgetClassName(widget->metaObject()->className()); 15 | //I don't use explicitly the string because if one day someone changes the name of the class, the compiler will output an error 16 | QString className(frame.metaObject()->className()); 17 | if (widgetClassName == className) { 18 | switch(code) { 19 | case PRESSED: 20 | emit mousePress(dynamic_cast (widget)); 21 | break; 22 | case RELEASED: 23 | emit mouseRelease(dynamic_cast (widget)); 24 | break; 25 | case MOVED: 26 | emit mouseMove(dynamic_cast (widget)); 27 | break; 28 | } 29 | } 30 | } 31 | } 32 | 33 | void QWidgetExtended::mousePressEvent(QMouseEvent *event) { 34 | signalEvent(event, PRESSED); 35 | } 36 | 37 | void QWidgetExtended::mouseReleaseEvent(QMouseEvent *event) { 38 | signalEvent(event, RELEASED); 39 | } 40 | 41 | void QWidgetExtended::mouseMoveEvent(QMouseEvent *event) { 42 | signalEvent(event, MOVED); 43 | } 44 | 45 | /* This method needs to be overrided to allow CSS styling also for QWidgetExtended */ 46 | void QWidgetExtended::paintEvent(QPaintEvent *pe) { 47 | QStyleOption opt; 48 | opt.initFrom(this); 49 | QStylePainter p(this); 50 | p.drawPrimitive(QStyle::PE_Widget, opt); 51 | } 52 | -------------------------------------------------------------------------------- /src/view/qwidget_extended.h: -------------------------------------------------------------------------------- 1 | #ifndef QWIDGETEXTENDED_H 2 | #define QWIDGETEXTENDED_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "qframe_extended.h" 12 | 13 | enum { PRESSED, RELEASED, MOVED }; 14 | 15 | class QWidgetExtended : public QWidget 16 | { 17 | Q_OBJECT 18 | 19 | private: 20 | void signalEvent(QMouseEvent *, int code); 21 | 22 | public: 23 | explicit QWidgetExtended(QWidget *parent = 0); 24 | 25 | protected: 26 | virtual void mousePressEvent(QMouseEvent *); 27 | virtual void mouseReleaseEvent(QMouseEvent *); 28 | virtual void mouseMoveEvent(QMouseEvent *); 29 | virtual void paintEvent(QPaintEvent *pe); 30 | 31 | signals: 32 | void mousePress(QFrameExtended *); 33 | void mouseRelease(QFrameExtended *); 34 | void mouseMove(QFrameExtended *); 35 | 36 | }; 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /src/view/settings.cpp: -------------------------------------------------------------------------------- 1 | #include "settings.h" 2 | 3 | #include 4 | 5 | const QString SettingsValues::past_months_expiration = "PastMonthsExpiration"; 6 | 7 | Settings::Settings(QWidget *parent) : 8 | QDialog(parent), 9 | ui(new Ui::Settings) 10 | { 11 | this->setWindowFlags(Qt::WindowCloseButtonHint | Qt::WindowMinimizeButtonHint); 12 | this->setWindowTitle("Settings"); 13 | QVBoxLayout *main_layout = new QVBoxLayout; 14 | QHBoxLayout *row1 = new QHBoxLayout; 15 | QLabel *lblPastMonthExpiration = new QLabel("Delete all events older than (months): "); 16 | this->pastMonthsExpiration = new QSpinBox; 17 | pastMonthsExpiration->setRange(0, 120); 18 | pastMonthsExpiration->setSingleStep(1); 19 | pastMonthsExpiration->setValue(2); 20 | pastMonthsExpiration->setFixedWidth(100); 21 | row1->addWidget(lblPastMonthExpiration); 22 | row1->addWidget(pastMonthsExpiration); 23 | QHBoxLayout *lastRow = new QHBoxLayout; 24 | QPushButton *btnCancel = new QPushButton("Cancel"); 25 | QPushButton *btnSave = new QPushButton("Save"); 26 | connect(btnCancel, &QPushButton::clicked, this, &Settings::on_button_cancel_click); 27 | connect(btnSave, &QPushButton::clicked, this, &Settings::on_button_save_click); 28 | lastRow->addWidget(btnCancel); 29 | lastRow->addWidget(btnSave); 30 | main_layout->addLayout(row1); 31 | main_layout->addLayout(lastRow); 32 | setLayout(main_layout); 33 | } 34 | 35 | Settings::~Settings() 36 | { 37 | delete ui; 38 | } 39 | 40 | void Settings::on_button_cancel_click() { 41 | this->close(); 42 | } 43 | 44 | void Settings::on_button_save_click() { 45 | QSettings settings; 46 | settings.setValue(SettingsValues::past_months_expiration, this->pastMonthsExpiration->value()); 47 | this->close(); 48 | } 49 | 50 | 51 | -------------------------------------------------------------------------------- /src/view/settings.h: -------------------------------------------------------------------------------- 1 | #ifndef SETTINGS_H 2 | #define SETTINGS_H 3 | 4 | #include "ui_settings.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "../persistence/securepmanager.h" 15 | 16 | namespace Ui { 17 | class Settings; 18 | } 19 | 20 | struct SettingsValues { 21 | /* initialization in settings.cpp */ 22 | static const QString past_months_expiration; 23 | }; 24 | 25 | class Settings : public QDialog 26 | { 27 | Q_OBJECT 28 | 29 | private: 30 | QSpinBox *pastMonthsExpiration; 31 | Ui::Settings *ui; 32 | 33 | public: 34 | explicit Settings(QWidget *parent = 0); 35 | ~Settings(); 36 | void on_button_cancel_click(); 37 | void on_button_save_click(); 38 | 39 | protected: 40 | 41 | }; 42 | 43 | #endif // SETTINGS_H 44 | -------------------------------------------------------------------------------- /src/view/ui_categorydialog.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | ** Form generated from reading UI file 'eventdialog.ui' 3 | ** 4 | ** Created by: Qt User Interface Compiler version 5.5.1 5 | ** 6 | ** WARNING! All changes made in this file will be lost when recompiling UI file! 7 | ********************************************************************************/ 8 | 9 | #ifndef UI_CATEGORYDIALOG_H 10 | #define UI_CATEGORYDIALOG_H 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | QT_BEGIN_NAMESPACE 20 | 21 | class Ui_CategoryDialog 22 | { 23 | public: 24 | 25 | void setupUi(QDialog *CategoryDialog) 26 | { 27 | if (CategoryDialog->objectName().isEmpty()) 28 | CategoryDialog->setObjectName(QStringLiteral("CategoryDialog")); 29 | CategoryDialog->resize(400, 300); 30 | 31 | retranslateUi(CategoryDialog); 32 | 33 | QMetaObject::connectSlotsByName(CategoryDialog); 34 | } // setupUi 35 | 36 | void retranslateUi(QDialog *CategoryDialog) 37 | { 38 | CategoryDialog->setWindowTitle(QApplication::translate("CategoryDialog", "Dialog", 0)); 39 | } // retranslateUi 40 | 41 | }; 42 | 43 | namespace Ui { 44 | class CategoryDialog: public Ui_CategoryDialog {}; 45 | } // namespace Ui 46 | 47 | QT_END_NAMESPACE 48 | 49 | #endif // UI_CATEGORYDIALOG_H 50 | -------------------------------------------------------------------------------- /src/view/ui_categoryeditdialog.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | ** Form generated from reading UI file 'eventdialog.ui' 3 | ** 4 | ** Created by: Qt User Interface Compiler version 5.5.1 5 | ** 6 | ** WARNING! All changes made in this file will be lost when recompiling UI file! 7 | ********************************************************************************/ 8 | 9 | #ifndef UI_CATEGORYEDITDIALOG_H 10 | #define UI_CATEGORYEDITDIALOG_H 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | QT_BEGIN_NAMESPACE 20 | 21 | class Ui_CategoryEditDialog 22 | { 23 | public: 24 | 25 | void setupUi(QDialog *CategoryEditDialog) 26 | { 27 | if (CategoryEditDialog->objectName().isEmpty()) 28 | CategoryEditDialog->setObjectName(QStringLiteral("CategoryEditDialog")); 29 | CategoryEditDialog->resize(400, 300); 30 | 31 | retranslateUi(CategoryEditDialog); 32 | 33 | QMetaObject::connectSlotsByName(CategoryEditDialog); 34 | } // setupUi 35 | 36 | void retranslateUi(QDialog *CategoryEditDialog) 37 | { 38 | CategoryEditDialog->setWindowTitle(QApplication::translate("CategoryEditDialog", "Dialog", 0)); 39 | } // retranslateUi 40 | 41 | }; 42 | 43 | namespace Ui { 44 | class CategoryEditDialog: public Ui_CategoryEditDialog {}; 45 | } // namespace Ui 46 | 47 | QT_END_NAMESPACE 48 | 49 | #endif // UI_CATEGORYEDITDIALOG_H 50 | -------------------------------------------------------------------------------- /src/view/ui_categoryselectdialog.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | ** Form generated from reading UI file 'eventdialog.ui' 3 | ** 4 | ** Created by: Qt User Interface Compiler version 5.5.1 5 | ** 6 | ** WARNING! All changes made in this file will be lost when recompiling UI file! 7 | ********************************************************************************/ 8 | 9 | #ifndef UI_CATEGORYSELECTDIALOG_H 10 | #define UI_CATEGORYSELECTDIALOG_H 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | QT_BEGIN_NAMESPACE 20 | 21 | class Ui_CategorySelectDialog 22 | { 23 | public: 24 | 25 | void setupUi(QDialog *CategorySelectDialog) 26 | { 27 | if (CategorySelectDialog->objectName().isEmpty()) 28 | CategorySelectDialog->setObjectName(QStringLiteral("CategorySelectDialog")); 29 | CategorySelectDialog->resize(400, 300); 30 | 31 | retranslateUi(CategorySelectDialog); 32 | 33 | QMetaObject::connectSlotsByName(CategorySelectDialog); 34 | } // setupUi 35 | 36 | void retranslateUi(QDialog *CategorySelectDialog) 37 | { 38 | CategorySelectDialog->setWindowTitle(QApplication::translate("CategorySelectDialog", "Dialog", 0)); 39 | } // retranslateUi 40 | 41 | }; 42 | 43 | namespace Ui { 44 | class CategorySelectDialog: public Ui_CategorySelectDialog {}; 45 | } // namespace Ui 46 | 47 | QT_END_NAMESPACE 48 | 49 | #endif // UI_CATEGORYSELECTDIALOG_H 50 | -------------------------------------------------------------------------------- /src/view/ui_customdialog.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | ** Form generated from reading UI file 'eventdialog.ui' 3 | ** 4 | ** Created by: Qt User Interface Compiler version 5.5.1 5 | ** 6 | ** WARNING! All changes made in this file will be lost when recompiling UI file! 7 | ********************************************************************************/ 8 | 9 | #ifndef UI_CUSTOMDIALOG_H 10 | #define UI_CUSTOMDIALOG_H 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | QT_BEGIN_NAMESPACE 20 | 21 | class Ui_CustomDialog 22 | { 23 | public: 24 | 25 | void setupUi(QDialog *CustomDialog) 26 | { 27 | if (CustomDialog->objectName().isEmpty()) 28 | CustomDialog->setObjectName(QStringLiteral("CustomDialog")); 29 | CustomDialog->resize(400, 300); 30 | 31 | CustomDialog->setWindowTitle(QApplication::translate("CustomDialog", "Dialog", 0)); 32 | 33 | QMetaObject::connectSlotsByName(CustomDialog); 34 | } // setupUi 35 | 36 | }; 37 | 38 | namespace Ui { 39 | class CustomDialog: public Ui_CustomDialog {}; 40 | } // namespace Ui 41 | 42 | QT_END_NAMESPACE 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /src/view/ui_eventdialog.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | ** Form generated from reading UI file 'eventdialog.ui' 3 | ** 4 | ** Created by: Qt User Interface Compiler version 5.5.1 5 | ** 6 | ** WARNING! All changes made in this file will be lost when recompiling UI file! 7 | ********************************************************************************/ 8 | 9 | #ifndef UI_EVENTDIALOG_H 10 | #define UI_EVENTDIALOG_H 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | QT_BEGIN_NAMESPACE 20 | 21 | class Ui_EventDialog 22 | { 23 | public: 24 | 25 | void setupUi(QDialog *EventDialog) 26 | { 27 | if (EventDialog->objectName().isEmpty()) 28 | EventDialog->setObjectName(QStringLiteral("EventDialog")); 29 | EventDialog->resize(400, 300); 30 | 31 | retranslateUi(EventDialog); 32 | 33 | QMetaObject::connectSlotsByName(EventDialog); 34 | } // setupUi 35 | 36 | void retranslateUi(QDialog *EventDialog) 37 | { 38 | EventDialog->setWindowTitle(QApplication::translate("EventDialog", "Dialog", 0)); 39 | } // retranslateUi 40 | 41 | }; 42 | 43 | namespace Ui { 44 | class EventDialog: public Ui_EventDialog {}; 45 | } // namespace Ui 46 | 47 | QT_END_NAMESPACE 48 | 49 | #endif // UI_EVENTDIALOG_H 50 | -------------------------------------------------------------------------------- /src/view/ui_kalendar.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | ** Form generated from reading UI file 'kalendar.ui' 3 | ** 4 | ** Created by: Qt User Interface Compiler version 5.5.1 5 | ** 6 | ** WARNING! All changes made in this file will be lost when recompiling UI file! 7 | ********************************************************************************/ 8 | 9 | #ifndef UI_KALENDAR_H 10 | #define UI_KALENDAR_H 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | QT_BEGIN_NAMESPACE 24 | 25 | class Ui_Kalendar 26 | { 27 | public: 28 | QMenuBar *menuBar; 29 | QToolBar *mainToolBar; 30 | QWidget *centralWidget; 31 | QStatusBar *statusBar; 32 | 33 | void setupUi(QMainWindow *Kalendar) 34 | { 35 | if (Kalendar->objectName().isEmpty()) 36 | Kalendar->setObjectName(QStringLiteral("Kalendar")); 37 | Kalendar->resize(400, 300); 38 | menuBar = new QMenuBar(Kalendar); 39 | menuBar->setObjectName(QStringLiteral("menuBar")); 40 | Kalendar->setMenuBar(menuBar); 41 | mainToolBar = new QToolBar(Kalendar); 42 | mainToolBar->setObjectName(QStringLiteral("mainToolBar")); 43 | Kalendar->addToolBar(mainToolBar); 44 | centralWidget = new QWidget(Kalendar); 45 | centralWidget->setObjectName(QStringLiteral("centralWidget")); 46 | Kalendar->setCentralWidget(centralWidget); 47 | statusBar = new QStatusBar(Kalendar); 48 | statusBar->setObjectName(QStringLiteral("statusBar")); 49 | Kalendar->setStatusBar(statusBar); 50 | 51 | retranslateUi(Kalendar); 52 | 53 | QMetaObject::connectSlotsByName(Kalendar); 54 | } // setupUi 55 | 56 | void retranslateUi(QMainWindow *Kalendar) 57 | { 58 | Kalendar->setWindowTitle(QApplication::translate("Kalendar", "Kalendar", 0)); 59 | } // retranslateUi 60 | 61 | }; 62 | 63 | namespace Ui { 64 | class Kalendar: public Ui_Kalendar {}; 65 | } // namespace Ui 66 | 67 | QT_END_NAMESPACE 68 | 69 | #endif // UI_KALENDAR_H 70 | -------------------------------------------------------------------------------- /src/view/ui_monthview.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | ** Form generated from reading UI file 'monthview.ui' 3 | ** 4 | ** Created by: Qt User Interface Compiler version 5.5.1 5 | ** 6 | ** WARNING! All changes made in this file will be lost when recompiling UI file! 7 | ********************************************************************************/ 8 | 9 | #ifndef UI_MONTHVIEW_H 10 | #define UI_MONTHVIEW_H 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | QT_BEGIN_NAMESPACE 23 | 24 | class Ui_MonthView 25 | { 26 | public: 27 | QMenuBar *menubar; 28 | QWidget *centralwidget; 29 | QStatusBar *statusbar; 30 | 31 | void setupUi(QMainWindow *MonthView) 32 | { 33 | if (MonthView->objectName().isEmpty()) 34 | MonthView->setObjectName(QStringLiteral("MonthView")); 35 | MonthView->resize(800, 600); 36 | menubar = new QMenuBar(MonthView); 37 | menubar->setObjectName(QStringLiteral("menubar")); 38 | MonthView->setMenuBar(menubar); 39 | centralwidget = new QWidget(MonthView); 40 | centralwidget->setObjectName(QStringLiteral("centralwidget")); 41 | MonthView->setCentralWidget(centralwidget); 42 | statusbar = new QStatusBar(MonthView); 43 | statusbar->setObjectName(QStringLiteral("statusbar")); 44 | MonthView->setStatusBar(statusbar); 45 | 46 | retranslateUi(MonthView); 47 | 48 | QMetaObject::connectSlotsByName(MonthView); 49 | } // setupUi 50 | 51 | void retranslateUi(QMainWindow *MonthView) 52 | { 53 | MonthView->setWindowTitle(QApplication::translate("MonthView", "MainWindow", 0)); 54 | } // retranslateUi 55 | 56 | }; 57 | 58 | namespace Ui { 59 | class MonthView: public Ui_MonthView {}; 60 | } // namespace Ui 61 | 62 | QT_END_NAMESPACE 63 | 64 | #endif // UI_MONTHVIEW_H 65 | -------------------------------------------------------------------------------- /src/view/ui_settings.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | ** Form generated from reading UI file 'eventdialog.ui' 3 | ** 4 | ** Created by: Qt User Interface Compiler version 5.5.1 5 | ** 6 | ** WARNING! All changes made in this file will be lost when recompiling UI file! 7 | ********************************************************************************/ 8 | 9 | #ifndef UI_CUSTOMDIALOG_H 10 | #define UI_CUSTOMDIALOG_H 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | QT_BEGIN_NAMESPACE 20 | 21 | class Ui_Settings 22 | { 23 | public: 24 | 25 | void setupUi(QDialog *Settings) 26 | { 27 | if (Settings->objectName().isEmpty()) 28 | Settings->setObjectName(QStringLiteral("Settings")); 29 | Settings->resize(400, 300); 30 | 31 | Settings->setWindowTitle(QApplication::translate("Settings", "Dialog", 0)); 32 | 33 | QMetaObject::connectSlotsByName(Settings); 34 | } // setupUi 35 | 36 | }; 37 | 38 | namespace Ui { 39 | class Settings: public Ui_Settings {}; 40 | } // namespace Ui 41 | 42 | QT_END_NAMESPACE 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /src/view/view.h: -------------------------------------------------------------------------------- 1 | #ifndef VIEW_H 2 | #define VIEW_H 3 | 4 | #include "model/date.h" 5 | #include "model/category.h" 6 | 7 | /* Abstract Class */ 8 | class View 9 | { 10 | public: 11 | //pure virtual functions 12 | virtual void refresh_events() = 0; 13 | virtual void display_events(Date date, Category *category) = 0; 14 | }; 15 | 16 | #endif // VIEW_H 17 | --------------------------------------------------------------------------------